From 449e8171f96a6a944d1f3b7d3627ae059eae21ca Mon Sep 17 00:00:00 2001 From: Vivek Goyal Date: Tue, 25 Jan 2022 13:51:14 -0500 Subject: [PATCH 001/460] virtiofsd: Drop membership of all supplementary groups (CVE-2022-0358) At the start, drop membership of all supplementary groups. This is not required. If we have membership of "root" supplementary group and when we switch uid/gid using setresuid/setsgid, we still retain membership of existing supplemntary groups. And that can allow some operations which are not normally allowed. For example, if root in guest creates a dir as follows. $ mkdir -m 03777 test_dir This sets SGID on dir as well as allows unprivileged users to write into this dir. And now as unprivileged user open file as follows. $ su test $ fd = open("test_dir/priviledge_id", O_RDWR|O_CREAT|O_EXCL, 02755); This will create SGID set executable in test_dir/. And that's a problem because now an unpriviliged user can execute it, get egid=0 and get access to resources owned by "root" group. This is privilege escalation. Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=2044863 Fixes: CVE-2022-0358 Reported-by: JIETAO XIAO Suggested-by: Miklos Szeredi Reviewed-by: Stefan Hajnoczi Reviewed-by: Dr. David Alan Gilbert Signed-off-by: Vivek Goyal Message-Id: Signed-off-by: Dr. David Alan Gilbert dgilbert: Fixed missing {}'s style nit --- tools/virtiofsd/passthrough_ll.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c index 64b5b4fbb1..b3d0674f6d 100644 --- a/tools/virtiofsd/passthrough_ll.c +++ b/tools/virtiofsd/passthrough_ll.c @@ -54,6 +54,7 @@ #include #include #include +#include #include "qemu/cutils.h" #include "passthrough_helpers.h" @@ -1161,6 +1162,30 @@ static void lo_lookup(fuse_req_t req, fuse_ino_t parent, const char *name) #define OURSYS_setresuid SYS_setresuid #endif +static void drop_supplementary_groups(void) +{ + int ret; + + ret = getgroups(0, NULL); + if (ret == -1) { + fuse_log(FUSE_LOG_ERR, "getgroups() failed with error=%d:%s\n", + errno, strerror(errno)); + exit(1); + } + + if (!ret) { + return; + } + + /* Drop all supplementary groups. We should not need it */ + ret = setgroups(0, NULL); + if (ret == -1) { + fuse_log(FUSE_LOG_ERR, "setgroups() failed with error=%d:%s\n", + errno, strerror(errno)); + exit(1); + } +} + /* * Change to uid/gid of caller so that file is created with * ownership of caller. @@ -3926,6 +3951,8 @@ int main(int argc, char *argv[]) qemu_init_exec_dir(argv[0]); + drop_supplementary_groups(); + pthread_mutex_init(&lo.mutex, NULL); lo.inodes = g_hash_table_new(lo_key_hash, lo_key_equal); lo.root.fd = -1; From 7e7237cd2bebede25f43a550ccb1219070da427e Mon Sep 17 00:00:00 2001 From: Victor Toso Date: Mon, 20 Dec 2021 15:56:24 +0100 Subject: [PATCH 002/460] schemas: add missing vim modeline Similar to f7160f3218 "schemas: Add vim modeline" Signed-off-by: Victor Toso Message-Id: <20211220145624.52801-1-victortoso@redhat.com> Acked-by: Markus Armbruster Signed-off-by: Markus Armbruster --- qapi/audio.json | 1 + qapi/compat.json | 1 + qapi/replay.json | 1 + qapi/trace.json | 1 + 4 files changed, 4 insertions(+) diff --git a/qapi/audio.json b/qapi/audio.json index 693e327c6b..0785e70a50 100644 --- a/qapi/audio.json +++ b/qapi/audio.json @@ -1,4 +1,5 @@ # -*- mode: python -*- +# vim: filetype=python # # Copyright (C) 2015-2019 Zoltán Kővágó # diff --git a/qapi/compat.json b/qapi/compat.json index dd7261ae2a..c53b69fe3f 100644 --- a/qapi/compat.json +++ b/qapi/compat.json @@ -1,4 +1,5 @@ # -*- Mode: Python -*- +# vim: filetype=python ## # = Compatibility policy diff --git a/qapi/replay.json b/qapi/replay.json index bfd83d7591..b4d1ba253b 100644 --- a/qapi/replay.json +++ b/qapi/replay.json @@ -1,4 +1,5 @@ # -*- Mode: Python -*- +# vim: filetype=python # ## diff --git a/qapi/trace.json b/qapi/trace.json index eedfded512..119509f565 100644 --- a/qapi/trace.json +++ b/qapi/trace.json @@ -1,4 +1,5 @@ # -*- mode: python -*- +# vim: filetype=python # # Copyright (C) 2011-2016 Lluís Vilanova # From 4e86df17326d2afaf74622c082d906ed3f96d1d7 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Wed, 26 Jan 2022 17:11:24 +0100 Subject: [PATCH 003/460] qapi/gen: Add FOO.trace-events output module We are going to generate trace events for QMP commands. We should generate both trace_*() function calls and trace-events files listing events for trace generator. So, add an output module FOO.trace-events for each FOO schema module. Since we're going to add trace events only to command marshallers, make the trace-events output optional, so we don't generate so many useless empty files. Currently nobody set add_trace_events to True, so new functionality is disabled. It will be enabled for QAPISchemaGenCommandVisitor in a further commit. Signed-off-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Stefan Hajnoczi Message-Id: <20220126161130.3240892-2-vsementsov@virtuozzo.com> Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster --- scripts/qapi/gen.py | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/scripts/qapi/gen.py b/scripts/qapi/gen.py index 995a97d2b8..113b49134d 100644 --- a/scripts/qapi/gen.py +++ b/scripts/qapi/gen.py @@ -192,6 +192,11 @@ class QAPIGenH(QAPIGenC): return guardend(self.fname) +class QAPIGenTrace(QAPIGen): + def _top(self) -> str: + return super()._top() + '# AUTOMATICALLY GENERATED, DO NOT MODIFY\n\n' + + @contextmanager def ifcontext(ifcond: QAPISchemaIfCond, *args: QAPIGenCCode) -> Iterator[None]: """ @@ -244,15 +249,18 @@ class QAPISchemaModularCVisitor(QAPISchemaVisitor): what: str, user_blurb: str, builtin_blurb: Optional[str], - pydoc: str): + pydoc: str, + gen_tracing: bool = False): self._prefix = prefix self._what = what self._user_blurb = user_blurb self._builtin_blurb = builtin_blurb self._pydoc = pydoc self._current_module: Optional[str] = None - self._module: Dict[str, Tuple[QAPIGenC, QAPIGenH]] = {} + self._module: Dict[str, Tuple[QAPIGenC, QAPIGenH, + Optional[QAPIGenTrace]]] = {} self._main_module: Optional[str] = None + self._gen_tracing = gen_tracing @property def _genc(self) -> QAPIGenC: @@ -264,6 +272,14 @@ class QAPISchemaModularCVisitor(QAPISchemaVisitor): assert self._current_module is not None return self._module[self._current_module][1] + @property + def _gen_trace_events(self) -> QAPIGenTrace: + assert self._gen_tracing + assert self._current_module is not None + gent = self._module[self._current_module][2] + assert gent is not None + return gent + @staticmethod def _module_dirname(name: str) -> str: if QAPISchemaModule.is_user_module(name): @@ -293,7 +309,12 @@ class QAPISchemaModularCVisitor(QAPISchemaVisitor): basename = self._module_filename(self._what, name) genc = QAPIGenC(basename + '.c', blurb, self._pydoc) genh = QAPIGenH(basename + '.h', blurb, self._pydoc) - self._module[name] = (genc, genh) + + gent: Optional[QAPIGenTrace] = None + if self._gen_tracing: + gent = QAPIGenTrace(basename + '.trace-events') + + self._module[name] = (genc, genh, gent) self._current_module = name @contextmanager @@ -304,11 +325,13 @@ class QAPISchemaModularCVisitor(QAPISchemaVisitor): self._current_module = old_module def write(self, output_dir: str, opt_builtins: bool = False) -> None: - for name, (genc, genh) in self._module.items(): + for name, (genc, genh, gent) in self._module.items(): if QAPISchemaModule.is_builtin_module(name) and not opt_builtins: continue genc.write(output_dir) genh.write(output_dir) + if gent is not None: + gent.write(output_dir) def _begin_builtin_module(self) -> None: pass From 167d913f34aa469fa30b0c51a04d8d2b9a5f1fa5 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Wed, 26 Jan 2022 17:11:25 +0100 Subject: [PATCH 004/460] qapi/commands: refactor error handling code Move error_propagate() to if (err) and make "if (err)" block mandatory. This is to simplify further commit, which will bring trace events generation for QMP commands. Signed-off-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Stefan Hajnoczi Message-Id: <20220126161130.3240892-3-vsementsov@virtuozzo.com> Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster --- docs/devel/qapi-code-gen.rst | 2 +- scripts/qapi/commands.py | 10 +++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/docs/devel/qapi-code-gen.rst b/docs/devel/qapi-code-gen.rst index a3b5473089..feafed79b5 100644 --- a/docs/devel/qapi-code-gen.rst +++ b/docs/devel/qapi-code-gen.rst @@ -1690,8 +1690,8 @@ Example:: } retval = qmp_my_command(arg.arg1, &err); - error_propagate(errp, err); if (err) { + error_propagate(errp, err); goto out; } diff --git a/scripts/qapi/commands.py b/scripts/qapi/commands.py index 21001bbd6b..17e5ed2414 100644 --- a/scripts/qapi/commands.py +++ b/scripts/qapi/commands.py @@ -74,14 +74,18 @@ def gen_call(name: str, ret = mcgen(''' %(lhs)sqmp_%(c_name)s(%(args)s&err); - error_propagate(errp, err); ''', c_name=c_name(name), args=argstr, lhs=lhs) - if ret_type: - ret += mcgen(''' + + ret += mcgen(''' if (err) { + error_propagate(errp, err); goto out; } +''') + + if ret_type: + ret += mcgen(''' qmp_marshal_output_%(c_name)s(retval, ret, errp); ''', From bd2017bc41566a94fbdfd8728b6e805ce2a2c124 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Wed, 26 Jan 2022 17:11:26 +0100 Subject: [PATCH 005/460] qapi/commands: Optionally generate trace for QMP commands Add trace generation disabled by default and new option --gen-trace to enable it. The next commit will enable it for qapi/, but not for qga/ and tests/. Making it work for the latter two would involve some Meson hackery to ensure we generate the trace-events files before trace-tool uses them. Since we don't actually support tracing there, we'll bypass that problem. Signed-off-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Stefan Hajnoczi Message-Id: <20220126161130.3240892-4-vsementsov@virtuozzo.com> Reviewed-by: Markus Armbruster [Superfluous #include dropped] Signed-off-by: Markus Armbruster --- scripts/qapi/commands.py | 90 +++++++++++++++++++++++++++++++++++----- scripts/qapi/main.py | 14 +++++-- 2 files changed, 90 insertions(+), 14 deletions(-) diff --git a/scripts/qapi/commands.py b/scripts/qapi/commands.py index 17e5ed2414..869d799ed2 100644 --- a/scripts/qapi/commands.py +++ b/scripts/qapi/commands.py @@ -53,7 +53,8 @@ def gen_command_decl(name: str, def gen_call(name: str, arg_type: Optional[QAPISchemaObjectType], boxed: bool, - ret_type: Optional[QAPISchemaType]) -> str: + ret_type: Optional[QAPISchemaType], + gen_tracing: bool) -> str: ret = '' argstr = '' @@ -71,14 +72,37 @@ def gen_call(name: str, if ret_type: lhs = 'retval = ' - ret = mcgen(''' + name = c_name(name) + upper = name.upper() - %(lhs)sqmp_%(c_name)s(%(args)s&err); + if gen_tracing: + ret += mcgen(''' + + if (trace_event_get_state_backends(TRACE_QMP_ENTER_%(upper)s)) { + g_autoptr(GString) req_json = qobject_to_json(QOBJECT(args)); + + trace_qmp_enter_%(name)s(req_json->str); + } + ''', + upper=upper, name=name) + + ret += mcgen(''' + + %(lhs)sqmp_%(name)s(%(args)s&err); ''', - c_name=c_name(name), args=argstr, lhs=lhs) + name=name, args=argstr, lhs=lhs) ret += mcgen(''' if (err) { +''') + + if gen_tracing: + ret += mcgen(''' + trace_qmp_exit_%(name)s(error_get_pretty(err), false); +''', + name=name) + + ret += mcgen(''' error_propagate(errp, err); goto out; } @@ -90,6 +114,25 @@ def gen_call(name: str, qmp_marshal_output_%(c_name)s(retval, ret, errp); ''', c_name=ret_type.c_name()) + + if gen_tracing: + if ret_type: + ret += mcgen(''' + + if (trace_event_get_state_backends(TRACE_QMP_EXIT_%(upper)s)) { + g_autoptr(GString) ret_json = qobject_to_json(*ret); + + trace_qmp_exit_%(name)s(ret_json->str, true); + } + ''', + upper=upper, name=name) + else: + ret += mcgen(''' + + trace_qmp_exit_%(name)s("{}", true); + ''', + name=name) + return ret @@ -126,10 +169,19 @@ def gen_marshal_decl(name: str) -> str: proto=build_marshal_proto(name)) +def gen_trace(name: str) -> str: + return mcgen(''' +qmp_enter_%(name)s(const char *json) "%%s" +qmp_exit_%(name)s(const char *result, bool succeeded) "%%s %%d" +''', + name=c_name(name)) + + def gen_marshal(name: str, arg_type: Optional[QAPISchemaObjectType], boxed: bool, - ret_type: Optional[QAPISchemaType]) -> str: + ret_type: Optional[QAPISchemaType], + gen_tracing: bool) -> str: have_args = boxed or (arg_type and not arg_type.is_empty()) if have_args: assert arg_type is not None @@ -184,7 +236,7 @@ def gen_marshal(name: str, } ''') - ret += gen_call(name, arg_type, boxed, ret_type) + ret += gen_call(name, arg_type, boxed, ret_type, gen_tracing) ret += mcgen(''' @@ -242,11 +294,13 @@ def gen_register_command(name: str, class QAPISchemaGenCommandVisitor(QAPISchemaModularCVisitor): - def __init__(self, prefix: str): + def __init__(self, prefix: str, gen_tracing: bool): super().__init__( prefix, 'qapi-commands', - ' * Schema-defined QAPI/QMP commands', None, __doc__) + ' * Schema-defined QAPI/QMP commands', None, __doc__, + gen_tracing=gen_tracing) self._visited_ret_types: Dict[QAPIGenC, Set[QAPISchemaType]] = {} + self._gen_tracing = gen_tracing def _begin_user_module(self, name: str) -> None: self._visited_ret_types[self._genc] = set() @@ -265,6 +319,16 @@ class QAPISchemaGenCommandVisitor(QAPISchemaModularCVisitor): ''', commands=commands, visit=visit)) + + if self._gen_tracing and commands != 'qapi-commands': + self._genc.add(mcgen(''' +#include "qapi/qmp/qjson.h" +#include "trace/trace-%(nm)s_trace_events.h" +''', + nm=c_name(commands, protect=False))) + # We use c_name(commands, protect=False) to turn '-' into '_', to + # match .underscorify() in trace/meson.build + self._genh.add(mcgen(''' #include "%(types)s.h" @@ -326,7 +390,10 @@ void %(c_prefix)sqmp_init_marshal(QmpCommandList *cmds) with ifcontext(ifcond, self._genh, self._genc): self._genh.add(gen_command_decl(name, arg_type, boxed, ret_type)) self._genh.add(gen_marshal_decl(name)) - self._genc.add(gen_marshal(name, arg_type, boxed, ret_type)) + self._genc.add(gen_marshal(name, arg_type, boxed, ret_type, + self._gen_tracing)) + if self._gen_tracing: + self._gen_trace_events.add(gen_trace(name)) with self._temp_module('./init'): with ifcontext(ifcond, self._genh, self._genc): self._genc.add(gen_register_command( @@ -336,7 +403,8 @@ void %(c_prefix)sqmp_init_marshal(QmpCommandList *cmds) def gen_commands(schema: QAPISchema, output_dir: str, - prefix: str) -> None: - vis = QAPISchemaGenCommandVisitor(prefix) + prefix: str, + gen_tracing: bool) -> None: + vis = QAPISchemaGenCommandVisitor(prefix, gen_tracing) schema.visit(vis) vis.write(output_dir) diff --git a/scripts/qapi/main.py b/scripts/qapi/main.py index f2ea6e0ce4..687d408aba 100644 --- a/scripts/qapi/main.py +++ b/scripts/qapi/main.py @@ -32,7 +32,8 @@ def generate(schema_file: str, output_dir: str, prefix: str, unmask: bool = False, - builtins: bool = False) -> None: + builtins: bool = False, + gen_tracing: bool = False) -> None: """ Generate C code for the given schema into the target directory. @@ -49,7 +50,7 @@ def generate(schema_file: str, schema = QAPISchema(schema_file) gen_types(schema, output_dir, prefix, builtins) gen_visit(schema, output_dir, prefix, builtins) - gen_commands(schema, output_dir, prefix) + gen_commands(schema, output_dir, prefix, gen_tracing) gen_events(schema, output_dir, prefix) gen_introspect(schema, output_dir, prefix, unmask) @@ -74,6 +75,12 @@ def main() -> int: parser.add_argument('-u', '--unmask-non-abi-names', action='store_true', dest='unmask', help="expose non-ABI names in introspection") + + # Option --gen-trace exists so we can avoid solving build system + # problems. TODO Drop it when we no longer need it. + parser.add_argument('--gen-trace', action='store_true', + help="add trace events to qmp marshals") + parser.add_argument('schema', action='store') args = parser.parse_args() @@ -88,7 +95,8 @@ def main() -> int: output_dir=args.output_dir, prefix=args.prefix, unmask=args.unmask, - builtins=args.builtins) + builtins=args.builtins, + gen_tracing=args.gen_trace) except QAPIError as err: print(f"{sys.argv[0]}: {str(err)}", file=sys.stderr) return 1 From b83a80e831137d57eadd3b91b74d06bf9d4a3f36 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Wed, 26 Jan 2022 17:11:27 +0100 Subject: [PATCH 006/460] meson: generate trace events for qmp commands 1. Use --gen-trace when generate qmp commands 2. Add corresponding .trace-events files as outputs in qapi_files custom target 3. Define global qapi_trace_events list of .trace-events file targets, to fill in trace/qapi.build and to use in trace/meson.build 4. In trace/meson.build use the new array as an additional source of .trace_events files to be processed Signed-off-by: Vladimir Sementsov-Ogievskiy Acked-by: Paolo Bonzini Reviewed-by: Stefan Hajnoczi Message-Id: <20220126161130.3240892-5-vsementsov@virtuozzo.com> Signed-off-by: Markus Armbruster --- meson.build | 3 +++ qapi/meson.build | 9 ++++++++- trace/meson.build | 11 ++++++++--- 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/meson.build b/meson.build index 833fd6bc4c..e0cfafe8d9 100644 --- a/meson.build +++ b/meson.build @@ -41,6 +41,7 @@ qemu_icondir = get_option('datadir') / 'icons' config_host_data = configuration_data() genh = [] +qapi_trace_events = [] target_dirs = config_host['TARGET_DIRS'].split() have_linux_user = false @@ -2557,6 +2558,8 @@ if 'CONFIG_VHOST_USER' in config_host vhost_user = libvhost_user.get_variable('vhost_user_dep') endif +# NOTE: the trace/ subdirectory needs the qapi_trace_events variable +# that is filled in by qapi/. subdir('qapi') subdir('qobject') subdir('stubs') diff --git a/qapi/meson.build b/qapi/meson.build index c0c49c15e4..b22558ca73 100644 --- a/qapi/meson.build +++ b/qapi/meson.build @@ -114,6 +114,7 @@ foreach module : qapi_all_modules 'qapi-events-@0@.h'.format(module), 'qapi-commands-@0@.c'.format(module), 'qapi-commands-@0@.h'.format(module), + 'qapi-commands-@0@.trace-events'.format(module), ] endif if module.endswith('-target') @@ -126,7 +127,7 @@ endforeach qapi_files = custom_target('shared QAPI source files', output: qapi_util_outputs + qapi_specific_outputs + qapi_nonmodule_outputs, input: [ files('qapi-schema.json') ], - command: [ qapi_gen, '-o', 'qapi', '-b', '@INPUT0@' ], + command: [ qapi_gen, '-o', 'qapi', '-b', '@INPUT0@', '--gen-trace' ], depend_files: [ qapi_inputs, qapi_gen_depends ]) # Now go through all the outputs and add them to the right sourceset. @@ -137,6 +138,9 @@ foreach output : qapi_util_outputs if output.endswith('.h') genh += qapi_files[i] endif + if output.endswith('.trace-events') + qapi_trace_events += qapi_files[i] + endif util_ss.add(qapi_files[i]) i = i + 1 endforeach @@ -145,6 +149,9 @@ foreach output : qapi_specific_outputs + qapi_nonmodule_outputs if output.endswith('.h') genh += qapi_files[i] endif + if output.endswith('.trace-events') + qapi_trace_events += qapi_files[i] + endif specific_ss.add(when: 'CONFIG_SOFTMMU', if_true: qapi_files[i]) i = i + 1 endforeach diff --git a/trace/meson.build b/trace/meson.build index 573dd699c6..c4794a1f2a 100644 --- a/trace/meson.build +++ b/trace/meson.build @@ -2,10 +2,15 @@ specific_ss.add(files('control-target.c')) trace_events_files = [] -foreach dir : [ '.' ] + trace_events_subdirs - trace_events_file = meson.project_source_root() / dir / 'trace-events' +foreach item : [ '.' ] + trace_events_subdirs + qapi_trace_events + if item in qapi_trace_events + trace_events_file = item + group_name = item.full_path().split('/')[-1].underscorify() + else + trace_events_file = meson.project_source_root() / item / 'trace-events' + group_name = item == '.' ? 'root' : item.underscorify() + endif trace_events_files += [ trace_events_file ] - group_name = dir == '.' ? 'root' : dir.underscorify() group = '--group=' + group_name fmt = '@0@-' + group_name + '.@1@' From 861aa79ad8bc1769b6eaf4bdde426decd3d947b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Thu, 13 Jan 2022 20:21:48 +0400 Subject: [PATCH 007/460] build-sys: fix a meson deprecation warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit WARNING: Deprecated features used: * 0.56.0: {'meson.source_root'} Signed-off-by: Marc-André Lureau Reported-by: Peter Maydell Message-Id: <20220113162148.3621818-1-marcandre.lureau@redhat.com> Signed-off-by: Paolo Bonzini --- 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 26937deb6d..842b1df420 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -103,7 +103,7 @@ if dbus_daemon.found() and config_host.has_key('GDBUS_CODEGEN') #qtests_i386 += ['dbus-vmstate-test'] dbus_vmstate1 = custom_target('dbus-vmstate description', output: ['dbus-vmstate1.h', 'dbus-vmstate1.c'], - input: meson.source_root() / 'backends/dbus-vmstate1.xml', + input: meson.project_source_root() / 'backends/dbus-vmstate1.xml', command: [config_host['GDBUS_CODEGEN'], '@INPUT@', '--interface-prefix', 'org.qemu', From 6bee09602100081c25e81573deb92cd22a6d7fec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Fri, 14 Jan 2022 12:43:11 +0400 Subject: [PATCH 008/460] build-sys: fix undefined ARCH error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ../qga/meson.build:76:4: ERROR: Key ARCH is not in the dictionary. Fixes commit 823eb013 ("configure, meson: move ARCH to meson.build") Signed-off-by: Marc-André Lureau Message-Id: <20220114084312.3725242-1-marcandre.lureau@redhat.com> Signed-off-by: Paolo Bonzini --- qga/meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qga/meson.build b/qga/meson.build index cfb1fbc085..613ecb9802 100644 --- a/qga/meson.build +++ b/qga/meson.build @@ -75,7 +75,7 @@ if targetos == 'windows' endif qga_msi = custom_target('QGA MSI', input: files('installer/qemu-ga.wxs'), - output: 'qemu-ga-@0@.msi'.format(config_host['ARCH']), + output: 'qemu-ga-@0@.msi'.format(host_arch), depends: deps, command: [ find_program('env'), From b422da4b3fb077469f3af480eb8c489abd480cf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Fri, 14 Jan 2022 12:43:12 +0400 Subject: [PATCH 009/460] docker: add msitools to Fedora/mingw cross MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit That should help catch build issues/regressions with wixl. Signed-off-by: Marc-André Lureau Message-Id: <20220114084312.3725242-2-marcandre.lureau@redhat.com> Signed-off-by: Paolo Bonzini --- tests/docker/dockerfiles/fedora-win32-cross.docker | 1 + tests/docker/dockerfiles/fedora-win64-cross.docker | 1 + 2 files changed, 2 insertions(+) diff --git a/tests/docker/dockerfiles/fedora-win32-cross.docker b/tests/docker/dockerfiles/fedora-win32-cross.docker index aad39dd97f..d80e66c651 100644 --- a/tests/docker/dockerfiles/fedora-win32-cross.docker +++ b/tests/docker/dockerfiles/fedora-win32-cross.docker @@ -29,6 +29,7 @@ ENV PACKAGES \ mingw32-pixman \ mingw32-pkg-config \ mingw32-SDL2 \ + msitools \ perl \ perl-Test-Harness \ python3 \ diff --git a/tests/docker/dockerfiles/fedora-win64-cross.docker b/tests/docker/dockerfiles/fedora-win64-cross.docker index 9a224a619b..2b12b94ccf 100644 --- a/tests/docker/dockerfiles/fedora-win64-cross.docker +++ b/tests/docker/dockerfiles/fedora-win64-cross.docker @@ -26,6 +26,7 @@ ENV PACKAGES \ mingw64-libusbx \ mingw64-pixman \ mingw64-pkg-config \ + msitools \ perl \ perl-Test-Harness \ python3 \ From 1206a1ec59e06f0107e40713d63e232135ea577e Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Sun, 16 Jan 2022 13:23:26 +0100 Subject: [PATCH 010/460] intc: Unexport InterruptStatsProviderClass-related functions The functions are only used within their respective source files, so no need for exporting. Signed-off-by: Bernhard Beschow Message-Id: <20220116122327.73048-1-shentey@gmail.com> Signed-off-by: Paolo Bonzini --- hw/intc/i8259_common.c | 6 +++--- hw/intc/ioapic_common.c | 2 +- include/hw/i386/ioapic_internal.h | 1 - include/hw/isa/i8259_internal.h | 3 --- 4 files changed, 4 insertions(+), 8 deletions(-) diff --git a/hw/intc/i8259_common.c b/hw/intc/i8259_common.c index d90b40fe4c..af2e4a2241 100644 --- a/hw/intc/i8259_common.c +++ b/hw/intc/i8259_common.c @@ -116,8 +116,8 @@ void pic_stat_update_irq(int irq, int level) } } -bool pic_get_statistics(InterruptStatsProvider *obj, - uint64_t **irq_counts, unsigned int *nb_irqs) +static bool pic_get_statistics(InterruptStatsProvider *obj, + uint64_t **irq_counts, unsigned int *nb_irqs) { PICCommonState *s = PIC_COMMON(obj); @@ -132,7 +132,7 @@ bool pic_get_statistics(InterruptStatsProvider *obj, return true; } -void pic_print_info(InterruptStatsProvider *obj, Monitor *mon) +static void pic_print_info(InterruptStatsProvider *obj, Monitor *mon) { PICCommonState *s = PIC_COMMON(obj); diff --git a/hw/intc/ioapic_common.c b/hw/intc/ioapic_common.c index 3cccfc1556..aa5f760871 100644 --- a/hw/intc/ioapic_common.c +++ b/hw/intc/ioapic_common.c @@ -76,7 +76,7 @@ static void ioapic_irr_dump(Monitor *mon, const char *name, uint32_t bitmap) monitor_printf(mon, "\n"); } -void ioapic_print_redtbl(Monitor *mon, IOAPICCommonState *s) +static void ioapic_print_redtbl(Monitor *mon, IOAPICCommonState *s) { static const char *delm_str[] = { "fixed", "lowest", "SMI", "...", "NMI", "INIT", "...", "extINT"}; diff --git a/include/hw/i386/ioapic_internal.h b/include/hw/i386/ioapic_internal.h index 021e715f11..9880443cc7 100644 --- a/include/hw/i386/ioapic_internal.h +++ b/include/hw/i386/ioapic_internal.h @@ -112,7 +112,6 @@ struct IOAPICCommonState { void ioapic_reset_common(DeviceState *dev); -void ioapic_print_redtbl(Monitor *mon, IOAPICCommonState *s); void ioapic_stat_update_irq(IOAPICCommonState *s, int irq, int level); #endif /* QEMU_IOAPIC_INTERNAL_H */ diff --git a/include/hw/isa/i8259_internal.h b/include/hw/isa/i8259_internal.h index a6ae8a583f..d272d879fb 100644 --- a/include/hw/isa/i8259_internal.h +++ b/include/hw/isa/i8259_internal.h @@ -72,8 +72,5 @@ struct PICCommonState { void pic_reset_common(PICCommonState *s); ISADevice *i8259_init_chip(const char *name, ISABus *bus, bool master); void pic_stat_update_irq(int irq, int level); -bool pic_get_statistics(InterruptStatsProvider *obj, - uint64_t **irq_counts, unsigned int *nb_irqs); -void pic_print_info(InterruptStatsProvider *obj, Monitor *mon); #endif /* QEMU_I8259_INTERNAL_H */ From de47b0ff40cae050a1ab674d7f4f097c12c527c6 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 18 Jan 2022 18:05:48 +0100 Subject: [PATCH 011/460] meson.build: Use a function from libfdt 1.5.1 for the library check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The fdt version test in meson.build uses a function from libfdt v1.4.7, but we require version 1.5.1 nowadays. Thus use a function that has been introduced in that version instead. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/822 Signed-off-by: Thomas Huth Reviewed-by: Alistair Francis Reviewed-by: David Gibson Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20220118170548.97288-1-thuth@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 833fd6bc4c..4429fd2041 100644 --- a/meson.build +++ b/meson.build @@ -2279,7 +2279,7 @@ if have_system if fdt.found() and cc.links(''' #include #include - int main(void) { fdt_check_full(NULL, 0); return 0; }''', + int main(void) { fdt_find_max_phandle(NULL, NULL); return 0; }''', dependencies: fdt) fdt_opt = 'system' elif fdt_opt == 'system' From b269a70810aabe1dd5b2e475a25976fb7d6a0f3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 20 Jan 2022 01:08:36 +0100 Subject: [PATCH 012/460] exec/cpu: Make host pages variables / macros 'target agnostic' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "host" pages are related to the *host* not the *target*, thus the qemu_host_page_size / qemu_host_page_mask variables and the HOST_PAGE_ALIGN() / REAL_HOST_PAGE_ALIGN() macros can be moved to "exec/cpu-common.h" which is target agnostic. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: David Hildenbrand Message-Id: <20220120000836.229419-1-f4bug@amsat.org> Signed-off-by: Paolo Bonzini --- include/exec/cpu-all.h | 9 --------- include/exec/cpu-common.h | 9 +++++++++ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/include/exec/cpu-all.h b/include/exec/cpu-all.h index bb37239efa..84caf5c3d9 100644 --- a/include/exec/cpu-all.h +++ b/include/exec/cpu-all.h @@ -234,15 +234,6 @@ extern const TargetPageBits target_page; #define TARGET_PAGE_ALIGN(addr) ROUND_UP((addr), TARGET_PAGE_SIZE) -/* Using intptr_t ensures that qemu_*_page_mask is sign-extended even - * when intptr_t is 32-bit and we are aligning a long long. - */ -extern uintptr_t qemu_host_page_size; -extern intptr_t qemu_host_page_mask; - -#define HOST_PAGE_ALIGN(addr) ROUND_UP((addr), qemu_host_page_size) -#define REAL_HOST_PAGE_ALIGN(addr) ROUND_UP((addr), qemu_real_host_page_size) - /* same as PROT_xxx */ #define PAGE_READ 0x0001 #define PAGE_WRITE 0x0002 diff --git a/include/exec/cpu-common.h b/include/exec/cpu-common.h index 039d422bf4..de5f444b19 100644 --- a/include/exec/cpu-common.h +++ b/include/exec/cpu-common.h @@ -7,6 +7,15 @@ #include "exec/hwaddr.h" #endif +/* Using intptr_t ensures that qemu_*_page_mask is sign-extended even + * when intptr_t is 32-bit and we are aligning a long long. + */ +extern uintptr_t qemu_host_page_size; +extern intptr_t qemu_host_page_mask; + +#define HOST_PAGE_ALIGN(addr) ROUND_UP((addr), qemu_host_page_size) +#define REAL_HOST_PAGE_ALIGN(addr) ROUND_UP((addr), qemu_real_host_page_size) + /* The CPU list lock nests outside page_(un)lock or mmap_(un)lock */ void qemu_init_cpu_list(void); void cpu_list_lock(void); From b1b2138753be4a0c9dc50975b080f8e7743b78a9 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 14 Jan 2022 15:37:30 +0000 Subject: [PATCH 013/460] linux-user: Remove unnecessary 'aligned' attribute from TaskState The linux-user struct TaskState has an 'aligned(16)' attribute. When the struct was first added in commit 851e67a1b46f in 2003, there was a justification in a comment (still present in the source today): /* NOTE: we force a big alignment so that the stack stored after is aligned too */ because the final field in the struct was "uint8_t stack[0];" But that field was removed in commit 48e15fc2d in 2010 which switched us to allocating the stack and the TaskState separately. Because we allocate the structure with g_new0() rather than as a local variable, the attribute made no difference to the alignment of the structure anyway. Remove the unnecessary attribute, and the corresponding comment. Signed-off-by: Peter Maydell Message-Id: <20220114153732.3767229-2-peter.maydell@linaro.org> Signed-off-by: Laurent Vivier --- linux-user/qemu.h | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/linux-user/qemu.h b/linux-user/qemu.h index 7910ce59cc..9d2b3119d1 100644 --- a/linux-user/qemu.h +++ b/linux-user/qemu.h @@ -94,10 +94,6 @@ struct emulated_sigtable { target_siginfo_t info; }; -/* - * NOTE: we force a big alignment so that the stack stored after is - * aligned too - */ typedef struct TaskState { pid_t ts_tid; /* tid (or pid) of this task */ #ifdef TARGET_ARM @@ -158,7 +154,7 @@ typedef struct TaskState { /* This thread's sigaltstack, if it has one */ struct target_sigaltstack sigaltstack_used; -} __attribute__((aligned(16))) TaskState; +} TaskState; abi_long do_brk(abi_ulong new_brk); From ff8e4827adb08b3db5ee5faacf4822a7b84b91be Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Wed, 26 Jan 2022 17:11:28 +0100 Subject: [PATCH 014/460] docs/qapi-code-gen: update to cover trace events code generation Previous commits enabled trace events generation for most of QAPI generated code (except for tests/ and qga/). Let's update documentation to illustrate it. Signed-off-by: Vladimir Sementsov-Ogievskiy Message-Id: <20220126161130.3240892-6-vsementsov@virtuozzo.com> Reviewed-by: Stefan Hajnoczi Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster --- docs/devel/qapi-code-gen.rst | 23 +++++++++++++++++++++++ docs/devel/tracing.rst | 2 ++ 2 files changed, 25 insertions(+) diff --git a/docs/devel/qapi-code-gen.rst b/docs/devel/qapi-code-gen.rst index feafed79b5..246709ede8 100644 --- a/docs/devel/qapi-code-gen.rst +++ b/docs/devel/qapi-code-gen.rst @@ -1630,6 +1630,9 @@ The following files are generated: ``$(prefix)qapi-commands.h`` Function prototypes for the QMP commands specified in the schema + ``$(prefix)qapi-commands.trace-events`` + Trace event declarations, see :ref:`tracing`. + ``$(prefix)qapi-init-commands.h`` Command initialization prototype @@ -1650,6 +1653,13 @@ Example:: void qmp_marshal_my_command(QDict *args, QObject **ret, Error **errp); #endif /* EXAMPLE_QAPI_COMMANDS_H */ + + $ cat qapi-generated/example-qapi-commands.trace-events + # AUTOMATICALLY GENERATED, DO NOT MODIFY + + qmp_enter_my_command(const char *json) "%s" + qmp_exit_my_command(const char *result, bool succeeded) "%s %d" + $ cat qapi-generated/example-qapi-commands.c [Uninteresting stuff omitted...] @@ -1689,14 +1699,27 @@ Example:: goto out; } + if (trace_event_get_state_backends(TRACE_QMP_ENTER_MY_COMMAND)) { + g_autoptr(GString) req_json = qobject_to_json(QOBJECT(args)); + + trace_qmp_enter_my_command(req_json->str); + } + retval = qmp_my_command(arg.arg1, &err); if (err) { + trace_qmp_exit_my_command(error_get_pretty(err), false); error_propagate(errp, err); goto out; } qmp_marshal_output_UserDefOne(retval, ret, errp); + if (trace_event_get_state_backends(TRACE_QMP_EXIT_MY_COMMAND)) { + g_autoptr(GString) ret_json = qobject_to_json(*ret); + + trace_qmp_exit_my_command(ret_json->str, true); + } + out: visit_free(v); v = qapi_dealloc_visitor_new(); diff --git a/docs/devel/tracing.rst b/docs/devel/tracing.rst index ba83954899..4290ac42ee 100644 --- a/docs/devel/tracing.rst +++ b/docs/devel/tracing.rst @@ -1,3 +1,5 @@ +.. _tracing: + ======= Tracing ======= From 378dfa482d6569b0034d21931570a4f31d940301 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Wed, 26 Jan 2022 17:11:29 +0100 Subject: [PATCH 015/460] meson: document why we don't generate trace events for tests/ and qga/ Making trace generation work for tests/ and qga/ would involve some Meson hackery to ensure we generate the trace-events files before trace-tool uses them. Since we don't actually support tracing there anyway, we bypass that problem. Let's add corresponding comments. Signed-off-by: Vladimir Sementsov-Ogievskiy Message-Id: <20220126161130.3240892-7-vsementsov@virtuozzo.com> Reviewed-by: Stefan Hajnoczi Reviewed-by: Markus Armbruster [Pasto fixed, commit message punctuation tidied up] Signed-off-by: Markus Armbruster --- qga/meson.build | 7 +++++++ tests/meson.build | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/qga/meson.build b/qga/meson.build index cfb1fbc085..724d5a667b 100644 --- a/qga/meson.build +++ b/qga/meson.build @@ -15,6 +15,13 @@ qga_qapi_outputs = [ 'qga-qapi-visit.h', ] +# Problem: to generate trace events, we'd have to add the .trace-events +# file to qapi_trace_events like we do in qapi/meson.build. Since +# qapi_trace_events is used by trace/meson.build, we'd have to move +# subdir('qga') above subdir('trace') in the top-level meson.build. +# Can't, because it would break the dependency of qga on qemuutil (which +# depends on trace_ss). Not worth solving now; simply suppress trace +# event generation instead. qga_qapi_files = custom_target('QGA QAPI files', output: qga_qapi_outputs, input: 'qapi-schema.json', diff --git a/tests/meson.build b/tests/meson.build index 3f3882748a..c8ab6272d1 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -31,6 +31,13 @@ test_qapi_outputs = [ 'test-qapi-visit.h', ] +# Problem: to generate trace events, we'd have to add the .trace-events +# file to qapi_trace_events like we do in qapi/meson.build. Since +# qapi_trace_events is used by trace/meson.build, we'd have to move +# subdir('tests') above subdir('trace') in the top-level meson.build. +# Can't, because it would break the dependency of qga on qemuutil (which +# depends on trace_ss). Not worth solving now; simply suppress trace +# event generation instead. test_qapi_files = custom_target('Test QAPI files', output: test_qapi_outputs, input: files('qapi-schema/qapi-schema-test.json', From 761a1a488e67a77858f3645a43fbdfe6208b51ce Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Wed, 26 Jan 2022 17:11:30 +0100 Subject: [PATCH 016/460] qapi: generate trace events by default We don't generate trace events for tests/ and qga/ because that it is not simple and not necessary. We have corresponding comments in both tests/meson.build and qga/meson.build. Still to not miss possible future qapi code generation call, and not to forget to enable trace events generation, let's enable it by default. So, turn option --gen-trace into opposite --no-trace-events and use new option only in tests/ and qga/ where we already have good comments why we don't generate trace events code. Note that this commit enables trace-events generation for qapi-gen.py call from tests/qapi-schema/meson.build and storage-daemon/meson.build. Still, both are kind of noop: tests/qapi-schema/ doesn't seem to generate any QMP command code and no .trace-events files anyway, storage-daemon/ uses common QMP command implementations and just generate empty .trace-events Signed-off-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Stefan Hajnoczi Message-Id: <20220126161130.3240892-8-vsementsov@virtuozzo.com> Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster --- qapi/meson.build | 2 +- qga/meson.build | 3 ++- scripts/qapi/main.py | 8 ++++---- tests/meson.build | 3 ++- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/qapi/meson.build b/qapi/meson.build index b22558ca73..656ef0e039 100644 --- a/qapi/meson.build +++ b/qapi/meson.build @@ -127,7 +127,7 @@ endforeach qapi_files = custom_target('shared QAPI source files', output: qapi_util_outputs + qapi_specific_outputs + qapi_nonmodule_outputs, input: [ files('qapi-schema.json') ], - command: [ qapi_gen, '-o', 'qapi', '-b', '@INPUT0@', '--gen-trace' ], + command: [ qapi_gen, '-o', 'qapi', '-b', '@INPUT0@' ], depend_files: [ qapi_inputs, qapi_gen_depends ]) # Now go through all the outputs and add them to the right sourceset. diff --git a/qga/meson.build b/qga/meson.build index 724d5a667b..f06b726ad3 100644 --- a/qga/meson.build +++ b/qga/meson.build @@ -25,7 +25,8 @@ qga_qapi_outputs = [ qga_qapi_files = custom_target('QGA QAPI files', output: qga_qapi_outputs, input: 'qapi-schema.json', - command: [ qapi_gen, '-o', 'qga', '-p', 'qga-', '@INPUT0@' ], + command: [ qapi_gen, '-o', 'qga', '-p', 'qga-', '@INPUT0@', + '--suppress-tracing' ], depend_files: qapi_gen_depends) qga_ss = ss.source_set() diff --git a/scripts/qapi/main.py b/scripts/qapi/main.py index 687d408aba..fc216a53d3 100644 --- a/scripts/qapi/main.py +++ b/scripts/qapi/main.py @@ -76,10 +76,10 @@ def main() -> int: dest='unmask', help="expose non-ABI names in introspection") - # Option --gen-trace exists so we can avoid solving build system + # Option --suppress-tracing exists so we can avoid solving build system # problems. TODO Drop it when we no longer need it. - parser.add_argument('--gen-trace', action='store_true', - help="add trace events to qmp marshals") + parser.add_argument('--suppress-tracing', action='store_true', + help="suppress adding trace events to qmp marshals") parser.add_argument('schema', action='store') args = parser.parse_args() @@ -96,7 +96,7 @@ def main() -> int: prefix=args.prefix, unmask=args.unmask, builtins=args.builtins, - gen_tracing=args.gen_trace) + gen_tracing=not args.suppress_tracing) except QAPIError as err: print(f"{sys.argv[0]}: {str(err)}", file=sys.stderr) return 1 diff --git a/tests/meson.build b/tests/meson.build index c8ab6272d1..94a425d94a 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -44,7 +44,8 @@ test_qapi_files = custom_target('Test QAPI files', 'qapi-schema/include/sub-module.json', 'qapi-schema/sub-sub-module.json'), command: [ qapi_gen, '-o', meson.current_build_dir(), - '-b', '-p', 'test-', '@INPUT0@' ], + '-b', '-p', 'test-', '@INPUT0@', + '--suppress-tracing' ], depend_files: qapi_gen_depends) # meson doesn't like generated output in other directories From 9288e803e61e8d56d1c6c6aa8beb58596fb84ed9 Mon Sep 17 00:00:00 2001 From: Jason Andryuk Date: Fri, 10 Dec 2021 14:34:34 -0500 Subject: [PATCH 017/460] xen-hvm: Allow disabling buffer_io_timer commit f37f29d31488 "xen: slightly simplify bufioreq handling" hard coded setting req.count = 1 during initial field setup before the main loop. This missed a subtlety that an early exit from the loop when there are no ioreqs to process, would have req.count == 0 for the return value. handle_buffered_io() would then remove state->buffered_io_timer. Instead handle_buffered_iopage() is basically always returning true and handle_buffered_io() always re-setting the timer. Restore the disabling of the timer by introducing a new handled_ioreq boolean and use as the return value. The named variable will more clearly show the intent of the code. Signed-off-by: Jason Andryuk Reviewed-by: Paul Durrant Message-Id: <20211210193434.75566-1-jandryuk@gmail.com> Signed-off-by: Anthony PERARD --- hw/i386/xen/xen-hvm.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/hw/i386/xen/xen-hvm.c b/hw/i386/xen/xen-hvm.c index 482be95415..cf8e500514 100644 --- a/hw/i386/xen/xen-hvm.c +++ b/hw/i386/xen/xen-hvm.c @@ -1087,10 +1087,11 @@ static void handle_ioreq(XenIOState *state, ioreq_t *req) } } -static int handle_buffered_iopage(XenIOState *state) +static bool handle_buffered_iopage(XenIOState *state) { buffered_iopage_t *buf_page = state->buffered_io_page; buf_ioreq_t *buf_req = NULL; + bool handled_ioreq = false; ioreq_t req; int qw; @@ -1144,9 +1145,10 @@ static int handle_buffered_iopage(XenIOState *state) assert(!req.data_is_ptr); qatomic_add(&buf_page->read_pointer, qw + 1); + handled_ioreq = true; } - return req.count; + return handled_ioreq; } static void handle_buffered_io(void *opaque) From a021a2dd8b790437d27db95774969349632f856a Mon Sep 17 00:00:00 2001 From: Ross Lagerwall Date: Mon, 24 Jan 2022 10:44:50 +0000 Subject: [PATCH 018/460] xen-mapcache: Avoid entry->lock overflow In some cases, a particular mapcache entry may be mapped 256 times causing the lock field to wrap to 0. For example, this may happen when using emulated NVME and the guest submits a large scatter-gather write. At this point, the entry map be remapped causing QEMU to write the wrong data or crash (since remap is not atomic). Avoid this overflow by increasing the lock field to a uint32_t and also detect it and abort rather than continuing regardless. Signed-off-by: Ross Lagerwall Reviewed-by: Paul Durrant Reviewed-by: Stefano Stabellini Message-Id: <20220124104450.152481-1-ross.lagerwall@citrix.com> Signed-off-by: Anthony PERARD --- hw/i386/xen/xen-mapcache.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/hw/i386/xen/xen-mapcache.c b/hw/i386/xen/xen-mapcache.c index bd47c3d672..f2ef977963 100644 --- a/hw/i386/xen/xen-mapcache.c +++ b/hw/i386/xen/xen-mapcache.c @@ -52,7 +52,7 @@ typedef struct MapCacheEntry { hwaddr paddr_index; uint8_t *vaddr_base; unsigned long *valid_mapping; - uint8_t lock; + uint32_t lock; #define XEN_MAPCACHE_ENTRY_DUMMY (1 << 0) uint8_t flags; hwaddr size; @@ -355,6 +355,12 @@ tryagain: if (lock) { MapCacheRev *reventry = g_malloc0(sizeof(MapCacheRev)); entry->lock++; + if (entry->lock == 0) { + fprintf(stderr, + "mapcache entry lock overflow: "TARGET_FMT_plx" -> %p\n", + entry->paddr_index, entry->vaddr_base); + abort(); + } reventry->dma = dma; reventry->vaddr_req = mapcache->last_entry->vaddr_base + address_offset; reventry->paddr_index = mapcache->last_entry->paddr_index; From ca9946d734147155d6c27b348920125f623a588f Mon Sep 17 00:00:00 2001 From: Serge Belyshev Date: Sat, 15 Jan 2022 14:32:35 +0300 Subject: [PATCH 019/460] linux-user/alpha: Fix target rlimits for alpha and rearrange for clarity Alpha uses different values of some TARGET_RLIMIT_* constants, which were missing and caused bugs like #577, fixed thus. Also rearranged all three (alpha, mips and sparc) that differ from everyone else for clarity. Signed-off-by: Serge Belyshev Resolves: https://gitlab.com/qemu-project/qemu/-/issues/577 Reviewed-by: Laurent Vivier Message-Id: <87y236lpwb.fsf@depni.sinp.msu.ru> [lv: replace tabs by spaces] Signed-off-by: Laurent Vivier --- linux-user/syscall_defs.h | 67 +++++++++++++++++++-------------------- 1 file changed, 32 insertions(+), 35 deletions(-) diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h index f23f0a2178..c8690688b5 100644 --- a/linux-user/syscall_defs.h +++ b/linux-user/syscall_defs.h @@ -730,44 +730,41 @@ struct target_rlimit { #define TARGET_RLIM_INFINITY ((abi_ulong)-1) #endif +#define TARGET_RLIMIT_CPU 0 +#define TARGET_RLIMIT_FSIZE 1 +#define TARGET_RLIMIT_DATA 2 +#define TARGET_RLIMIT_STACK 3 +#define TARGET_RLIMIT_CORE 4 #if defined(TARGET_MIPS) -#define TARGET_RLIMIT_CPU 0 -#define TARGET_RLIMIT_FSIZE 1 -#define TARGET_RLIMIT_DATA 2 -#define TARGET_RLIMIT_STACK 3 -#define TARGET_RLIMIT_CORE 4 -#define TARGET_RLIMIT_RSS 7 -#define TARGET_RLIMIT_NPROC 8 -#define TARGET_RLIMIT_NOFILE 5 -#define TARGET_RLIMIT_MEMLOCK 9 -#define TARGET_RLIMIT_AS 6 -#define TARGET_RLIMIT_LOCKS 10 -#define TARGET_RLIMIT_SIGPENDING 11 -#define TARGET_RLIMIT_MSGQUEUE 12 -#define TARGET_RLIMIT_NICE 13 -#define TARGET_RLIMIT_RTPRIO 14 +#define TARGET_RLIMIT_NOFILE 5 +#define TARGET_RLIMIT_AS 6 +#define TARGET_RLIMIT_RSS 7 +#define TARGET_RLIMIT_NPROC 8 +#define TARGET_RLIMIT_MEMLOCK 9 +#elif defined(TARGET_ALPHA) +#define TARGET_RLIMIT_RSS 5 +#define TARGET_RLIMIT_NOFILE 6 +#define TARGET_RLIMIT_AS 7 +#define TARGET_RLIMIT_NPROC 8 +#define TARGET_RLIMIT_MEMLOCK 9 +#elif defined(TARGET_SPARC) +#define TARGET_RLIMIT_RSS 5 +#define TARGET_RLIMIT_NOFILE 6 +#define TARGET_RLIMIT_NPROC 7 +#define TARGET_RLIMIT_MEMLOCK 8 +#define TARGET_RLIMIT_AS 9 #else -#define TARGET_RLIMIT_CPU 0 -#define TARGET_RLIMIT_FSIZE 1 -#define TARGET_RLIMIT_DATA 2 -#define TARGET_RLIMIT_STACK 3 -#define TARGET_RLIMIT_CORE 4 -#define TARGET_RLIMIT_RSS 5 -#if defined(TARGET_SPARC) -#define TARGET_RLIMIT_NOFILE 6 -#define TARGET_RLIMIT_NPROC 7 -#else -#define TARGET_RLIMIT_NPROC 6 -#define TARGET_RLIMIT_NOFILE 7 -#endif -#define TARGET_RLIMIT_MEMLOCK 8 -#define TARGET_RLIMIT_AS 9 -#define TARGET_RLIMIT_LOCKS 10 -#define TARGET_RLIMIT_SIGPENDING 11 -#define TARGET_RLIMIT_MSGQUEUE 12 -#define TARGET_RLIMIT_NICE 13 -#define TARGET_RLIMIT_RTPRIO 14 +#define TARGET_RLIMIT_RSS 5 +#define TARGET_RLIMIT_NPROC 6 +#define TARGET_RLIMIT_NOFILE 7 +#define TARGET_RLIMIT_MEMLOCK 8 +#define TARGET_RLIMIT_AS 9 #endif +#define TARGET_RLIMIT_LOCKS 10 +#define TARGET_RLIMIT_SIGPENDING 11 +#define TARGET_RLIMIT_MSGQUEUE 12 +#define TARGET_RLIMIT_NICE 13 +#define TARGET_RLIMIT_RTPRIO 14 struct target_pollfd { int fd; /* file descriptor */ From 33f53ac52ab664f3d6ec34047c0ae21e32fa26b4 Mon Sep 17 00:00:00 2001 From: Paul Brook Date: Wed, 26 Jan 2022 20:26:36 +0000 Subject: [PATCH 020/460] linux-user: Fix inotify on aarch64 The inotify implementation originally called the raw host syscalls. Commit 3b3f24add0 changed this to use the glibc wrappers. However ifdefs in syscall.c still test for presence of the raw syscalls. This causes a problem on e.g. aarch64 hosts which never had the inotify_init syscall - it had been obsoleted by inotify_init1 before aarch64 was invented! However it does have a perfectly good glibc implementation of inotify_wait. Fix this by removing all the raw __NR_inotify_* tests, and instead check CONFIG_INOTIFY, which already tests for the glibc functionality we use. Also remove the now-pointless sys_inotify* wrappers. Tested using x86-64 inotifywatch on aarch64 host, and vice-versa Signed-off-by: Paul Brook Reviewed-by: Laurent Vivier Message-Id: <20220126202636.655289-1-paul@nowt.org> Signed-off-by: Laurent Vivier --- linux-user/fd-trans.c | 5 ++--- linux-user/syscall.c | 50 +++++++++---------------------------------- 2 files changed, 12 insertions(+), 43 deletions(-) diff --git a/linux-user/fd-trans.c b/linux-user/fd-trans.c index a17d05c079..7b25468d02 100644 --- a/linux-user/fd-trans.c +++ b/linux-user/fd-trans.c @@ -1644,9 +1644,8 @@ TargetFdTrans target_eventfd_trans = { .target_to_host_data = swap_data_eventfd, }; -#if (defined(TARGET_NR_inotify_init) && defined(__NR_inotify_init)) || \ - (defined(CONFIG_INOTIFY1) && defined(TARGET_NR_inotify_init1) && \ - defined(__NR_inotify_init1)) +#if defined(CONFIG_INOTIFY) && (defined(TARGET_NR_inotify_init) || \ + defined(TARGET_NR_inotify_init1)) static abi_long host_to_target_data_inotify(void *buf, size_t len) { struct inotify_event *ev; diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 5950222a77..2ca0f086db 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -272,9 +272,6 @@ static type name (type1 arg1,type2 arg2,type3 arg3,type4 arg4,type5 arg5, \ #if defined(__NR_futex_time64) # define __NR_sys_futex_time64 __NR_futex_time64 #endif -#define __NR_sys_inotify_init __NR_inotify_init -#define __NR_sys_inotify_add_watch __NR_inotify_add_watch -#define __NR_sys_inotify_rm_watch __NR_inotify_rm_watch #define __NR_sys_statx __NR_statx #if defined(__alpha__) || defined(__x86_64__) || defined(__s390x__) @@ -477,33 +474,6 @@ static int sys_renameat2(int oldfd, const char *old, #ifdef CONFIG_INOTIFY #include - -#if defined(TARGET_NR_inotify_init) && defined(__NR_inotify_init) -static int sys_inotify_init(void) -{ - return (inotify_init()); -} -#endif -#if defined(TARGET_NR_inotify_add_watch) && defined(__NR_inotify_add_watch) -static int sys_inotify_add_watch(int fd,const char *pathname, int32_t mask) -{ - return (inotify_add_watch(fd, pathname, mask)); -} -#endif -#if defined(TARGET_NR_inotify_rm_watch) && defined(__NR_inotify_rm_watch) -static int sys_inotify_rm_watch(int fd, int32_t wd) -{ - return (inotify_rm_watch(fd, wd)); -} -#endif -#ifdef CONFIG_INOTIFY1 -#if defined(TARGET_NR_inotify_init1) && defined(__NR_inotify_init1) -static int sys_inotify_init1(int flags) -{ - return (inotify_init1(flags)); -} -#endif -#endif #else /* Userspace can usually survive runtime without inotify */ #undef TARGET_NR_inotify_init @@ -12341,35 +12311,35 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1, case TARGET_NR_futex_time64: return do_futex_time64(cpu, arg1, arg2, arg3, arg4, arg5, arg6); #endif -#if defined(TARGET_NR_inotify_init) && defined(__NR_inotify_init) +#ifdef CONFIG_INOTIFY +#if defined(TARGET_NR_inotify_init) case TARGET_NR_inotify_init: - ret = get_errno(sys_inotify_init()); + ret = get_errno(inotify_init()); if (ret >= 0) { fd_trans_register(ret, &target_inotify_trans); } return ret; #endif -#ifdef CONFIG_INOTIFY1 -#if defined(TARGET_NR_inotify_init1) && defined(__NR_inotify_init1) +#if defined(TARGET_NR_inotify_init1) && defined(CONFIG_INOTIFY1) case TARGET_NR_inotify_init1: - ret = get_errno(sys_inotify_init1(target_to_host_bitmask(arg1, + ret = get_errno(inotify_init1(target_to_host_bitmask(arg1, fcntl_flags_tbl))); if (ret >= 0) { fd_trans_register(ret, &target_inotify_trans); } return ret; #endif -#endif -#if defined(TARGET_NR_inotify_add_watch) && defined(__NR_inotify_add_watch) +#if defined(TARGET_NR_inotify_add_watch) case TARGET_NR_inotify_add_watch: p = lock_user_string(arg2); - ret = get_errno(sys_inotify_add_watch(arg1, path(p), arg3)); + ret = get_errno(inotify_add_watch(arg1, path(p), arg3)); unlock_user(p, arg2, 0); return ret; #endif -#if defined(TARGET_NR_inotify_rm_watch) && defined(__NR_inotify_rm_watch) +#if defined(TARGET_NR_inotify_rm_watch) case TARGET_NR_inotify_rm_watch: - return get_errno(sys_inotify_rm_watch(arg1, arg2)); + return get_errno(inotify_rm_watch(arg1, arg2)); +#endif #endif #if defined(TARGET_NR_mq_open) && defined(__NR_mq_open) From d3ced2a59a543b3c3fc7dee2e64ee360e9a698cd Mon Sep 17 00:00:00 2001 From: Shu-Chun Weng Date: Wed, 26 Jan 2022 13:25:58 -0800 Subject: [PATCH 021/460] linux-user: rt_sigprocmask, check read perms first Linux kernel does it this way (checks read permission before validating `how`) and the latest version of ABSL's `AddressIsReadable()` depends on this behavior. c.f. https://github.com/torvalds/linux/blob/9539ba4308ad5bdca6cb41c7b73cbb9f796dcdd7/kernel/signal.c#L3147 Reviewed-by: Patrick Venture Signed-off-by: Shu-Chun Weng Reviewed-by: Laurent Vivier Signed-off-by: Patrick Venture Message-Id: <20220126212559.1936290-2-venture@google.com> Signed-off-by: Laurent Vivier --- linux-user/syscall.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 2ca0f086db..9f8b497fa3 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -9478,6 +9478,13 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1, } if (arg2) { + p = lock_user(VERIFY_READ, arg2, sizeof(target_sigset_t), 1); + if (!p) { + return -TARGET_EFAULT; + } + target_to_host_sigset(&set, p); + unlock_user(p, arg2, 0); + set_ptr = &set; switch(how) { case TARGET_SIG_BLOCK: how = SIG_BLOCK; @@ -9491,11 +9498,6 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1, default: return -TARGET_EINVAL; } - if (!(p = lock_user(VERIFY_READ, arg2, sizeof(target_sigset_t), 1))) - return -TARGET_EFAULT; - target_to_host_sigset(&set, p); - unlock_user(p, arg2, 0); - set_ptr = &set; } else { how = 0; set_ptr = NULL; From ebce1719ac0a2a71b64742ecf1c9ec2497a65a55 Mon Sep 17 00:00:00 2001 From: Patrick Venture Date: Wed, 26 Jan 2022 13:25:59 -0800 Subject: [PATCH 022/460] linux-user: sigprocmask check read perms first Linux kernel now checks the read permissions before validating `how` Suggested-by: Laurent Vivier Signed-off-by: Patrick Venture Reviewed-by: Laurent Vivier Message-Id: <20220126212559.1936290-3-venture@google.com> [lv: remove unneeded ")"] Signed-off-by: Laurent Vivier --- linux-user/syscall.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 9f8b497fa3..84cfa223df 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -9435,6 +9435,13 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1, int how; if (arg2) { + p = lock_user(VERIFY_READ, arg2, sizeof(target_sigset_t), 1); + if (!p) { + return -TARGET_EFAULT; + } + target_to_host_old_sigset(&set, p); + unlock_user(p, arg2, 0); + set_ptr = &set; switch (arg1) { case TARGET_SIG_BLOCK: how = SIG_BLOCK; @@ -9448,11 +9455,6 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1, default: return -TARGET_EINVAL; } - if (!(p = lock_user(VERIFY_READ, arg2, sizeof(target_sigset_t), 1))) - return -TARGET_EFAULT; - target_to_host_old_sigset(&set, p); - unlock_user(p, arg2, 0); - set_ptr = &set; } else { how = 0; set_ptr = NULL; From eb33cdaeda55d951f915152ad23816d47aca9955 Mon Sep 17 00:00:00 2001 From: Cameron Esfahani Date: Thu, 27 Jan 2022 16:12:51 -0800 Subject: [PATCH 023/460] linux-user: Implement starttime field in self stat emulation Instead of always returning 0, return actual starttime. Signed-off-by: Cameron Esfahani Reviewed-by: Laurent Vivier Message-Id: <20220128001251.45165-1-dirty@apple.com> Signed-off-by: Laurent Vivier --- linux-user/main.c | 14 ++++++++++++++ linux-user/qemu.h | 3 +++ linux-user/syscall.c | 3 +++ 3 files changed, 20 insertions(+) diff --git a/linux-user/main.c b/linux-user/main.c index 16def5215d..fbc9bcfd5f 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -190,12 +190,26 @@ void stop_all_tasks(void) /* Assumes contents are already zeroed. */ void init_task_state(TaskState *ts) { + long ticks_per_sec; + struct timespec bt; + ts->used = 1; ts->sigaltstack_used = (struct target_sigaltstack) { .ss_sp = 0, .ss_size = 0, .ss_flags = TARGET_SS_DISABLE, }; + + /* Capture task start time relative to system boot */ + + ticks_per_sec = sysconf(_SC_CLK_TCK); + + if ((ticks_per_sec > 0) && !clock_gettime(CLOCK_BOOTTIME, &bt)) { + /* start_boottime is expressed in clock ticks */ + ts->start_boottime = bt.tv_sec * (uint64_t) ticks_per_sec; + ts->start_boottime += bt.tv_nsec * (uint64_t) ticks_per_sec / + NANOSECONDS_PER_SECOND; + } } CPUArchState *cpu_copy(CPUArchState *env) diff --git a/linux-user/qemu.h b/linux-user/qemu.h index 9d2b3119d1..98dfbf2096 100644 --- a/linux-user/qemu.h +++ b/linux-user/qemu.h @@ -154,6 +154,9 @@ typedef struct TaskState { /* This thread's sigaltstack, if it has one */ struct target_sigaltstack sigaltstack_used; + + /* Start time of task after system boot in clock ticks */ + uint64_t start_boottime; } TaskState; abi_long do_brk(abi_ulong new_brk); diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 84cfa223df..b3948d13a9 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -8077,6 +8077,9 @@ static int open_self_stat(void *cpu_env, int fd) } else if (i == 3) { /* ppid */ g_string_printf(buf, FMT_pid " ", getppid()); + } else if (i == 21) { + /* starttime */ + g_string_printf(buf, "%" PRIu64 " ", ts->start_boottime); } else if (i == 27) { /* stack bottom */ g_string_printf(buf, TARGET_ABI_FMT_ld " ", ts->info->start_stack); From 235b523dba80bec984a96ffc83893d0ccff18312 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 22 Jan 2022 01:20:52 +0100 Subject: [PATCH 024/460] meson: Use find_program() to resolve the entitlement.sh script MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Using ../configure without any particular option generates 31 targets on Darwin, and meson search for the entitlement.sh script 31 times: Program nm found: YES Program scripts/undefsym.py found: YES (/opt/homebrew/opt/python@3.9/bin/python3.9 /Code/qemu/scripts/undefsym.py) Program scripts/feature_to_c.sh found: YES (/bin/sh /Code/qemu/scripts/feature_to_c.sh) Program scripts/entitlement.sh found: YES (/Code/qemu/scripts/entitlement.sh) Program scripts/entitlement.sh found: YES (/Code/qemu/scripts/entitlement.sh) Program scripts/entitlement.sh found: YES (/Code/qemu/scripts/entitlement.sh) Program scripts/entitlement.sh found: YES (/Code/qemu/scripts/entitlement.sh) Program scripts/entitlement.sh found: YES (/Code/qemu/scripts/entitlement.sh) Program scripts/entitlement.sh found: YES (/Code/qemu/scripts/entitlement.sh) Program scripts/entitlement.sh found: YES (/Code/qemu/scripts/entitlement.sh) Program scripts/entitlement.sh found: YES (/Code/qemu/scripts/entitlement.sh) Program scripts/entitlement.sh found: YES (/Code/qemu/scripts/entitlement.sh) Program scripts/entitlement.sh found: YES (/Code/qemu/scripts/entitlement.sh) Program scripts/entitlement.sh found: YES (/Code/qemu/scripts/entitlement.sh) Program scripts/entitlement.sh found: YES (/Code/qemu/scripts/entitlement.sh) Program scripts/entitlement.sh found: YES (/Code/qemu/scripts/entitlement.sh) Program scripts/entitlement.sh found: YES (/Code/qemu/scripts/entitlement.sh) Program scripts/entitlement.sh found: YES (/Code/qemu/scripts/entitlement.sh) Program scripts/entitlement.sh found: YES (/Code/qemu/scripts/entitlement.sh) Program scripts/entitlement.sh found: YES (/Code/qemu/scripts/entitlement.sh) Program scripts/entitlement.sh found: YES (/Code/qemu/scripts/entitlement.sh) Program scripts/entitlement.sh found: YES (/Code/qemu/scripts/entitlement.sh) Program scripts/entitlement.sh found: YES (/Code/qemu/scripts/entitlement.sh) Program scripts/entitlement.sh found: YES (/Code/qemu/scripts/entitlement.sh) Program scripts/entitlement.sh found: YES (/Code/qemu/scripts/entitlement.sh) Program scripts/entitlement.sh found: YES (/Code/qemu/scripts/entitlement.sh) Program scripts/entitlement.sh found: YES (/Code/qemu/scripts/entitlement.sh) Program scripts/entitlement.sh found: YES (/Code/qemu/scripts/entitlement.sh) Program scripts/entitlement.sh found: YES (/Code/qemu/scripts/entitlement.sh) Program scripts/entitlement.sh found: YES (/Code/qemu/scripts/entitlement.sh) Program scripts/entitlement.sh found: YES (/Code/qemu/scripts/entitlement.sh) Program scripts/entitlement.sh found: YES (/Code/qemu/scripts/entitlement.sh) Program scripts/entitlement.sh found: YES (/Code/qemu/scripts/entitlement.sh) Program scripts/entitlement.sh found: YES (/Code/qemu/scripts/entitlement.sh) Configuring 50-edk2-i386-secure.json using configuration Configuring 50-edk2-x86_64-secure.json using configuration Use find_program() which seems to cache the script path once found. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Akihiko Odaki Reviewed-by: Richard Henderson Message-Id: <20220122002052.83745-1-f4bug@amsat.org> Signed-off-by: Paolo Bonzini --- meson.build | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/meson.build b/meson.build index 4429fd2041..c69fbbd0cb 100644 --- a/meson.build +++ b/meson.build @@ -3052,17 +3052,14 @@ foreach target : target_dirs install_input += meson.current_source_dir() / entitlements endif + entitlement = find_program('scripts/entitlement.sh') emulators += {exe['name'] : custom_target(exe['name'], input: build_input, output: exe['name'], - command: [ - files('scripts/entitlement.sh'), - '@OUTPUT@', - '@INPUT@' - ]) + command: [entitlement, '@OUTPUT@', '@INPUT@']) } - meson.add_install_script('scripts/entitlement.sh', '--install', + meson.add_install_script(entitlement, '--install', get_option('bindir') / exe['name'], install_input) else From bcda7b178fde7797f476e3b066fe5fc76bfa1c43 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Thu, 23 Dec 2021 19:39:33 +0100 Subject: [PATCH 025/460] check-block.sh: passthrough -jN flag of make to -j N flag of check This improves performance of running iotests during "make -jN check". Signed-off-by: Vladimir Sementsov-Ogievskiy Message-Id: <20211223183933.1497037-1-vsementsov@virtuozzo.com> Signed-off-by: Paolo Bonzini --- tests/check-block.sh | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tests/check-block.sh b/tests/check-block.sh index f86cb863de..d98d49ad63 100755 --- a/tests/check-block.sh +++ b/tests/check-block.sh @@ -74,10 +74,17 @@ cd tests/qemu-iotests # QEMU_CHECK_BLOCK_AUTO is used to disable some unstable sub-tests export QEMU_CHECK_BLOCK_AUTO=1 export PYTHONUTF8=1 +# If make was called with -jN we want to call ./check with -j N. Extract the +# flag from MAKEFLAGS, so that if it absent (or MAKEFLAGS is not defined), JOBS +# would be an empty line otherwise JOBS is prepared string of flag with value: +# "-j N" +# Note, that the following works even if make was called with "-j N" or even +# "--jobs N", as all these variants becomes simply "-jN" in MAKEFLAGS variable. +JOBS=$(echo "$MAKEFLAGS" | sed -n 's/\(^\|.* \)-j\([0-9]\+\)\( .*\|$\)/-j \2/p') ret=0 for fmt in $format_list ; do - ${PYTHON} ./check -makecheck -$fmt $group || ret=1 + ${PYTHON} ./check $JOBS -makecheck -$fmt $group || ret=1 done exit $ret From 3e233e29178dd9ebd3b3c3382a2e0ebea9e8127d Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 9 Nov 2021 14:13:00 +0100 Subject: [PATCH 026/460] scripts/mtest2make: add support for SPEED=thorough Signed-off-by: Paolo Bonzini --- meson.build | 5 +++-- scripts/mtest2make.py | 10 +++++++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/meson.build b/meson.build index c69fbbd0cb..f3f7b9c191 100644 --- a/meson.build +++ b/meson.build @@ -3,8 +3,9 @@ project('qemu', ['c'], meson_version: '>=0.58.2', 'b_staticpic=false', 'stdsplit=false'], version: files('VERSION')) -add_test_setup('quick', exclude_suites: 'slow', is_default: true) -add_test_setup('slow', env: ['G_TEST_SLOW=1', 'SPEED=slow']) +add_test_setup('quick', exclude_suites: ['slow', 'thorough'], is_default: true) +add_test_setup('slow', exclude_suites: ['thorough'], env: ['G_TEST_SLOW=1', 'SPEED=slow']) +add_test_setup('thorough', env: ['G_TEST_SLOW=1', 'SPEED=thorough']) not_found = dependency('', required: false) keyval = import('keyval') diff --git a/scripts/mtest2make.py b/scripts/mtest2make.py index 7067bdadf5..4b9c561b30 100644 --- a/scripts/mtest2make.py +++ b/scripts/mtest2make.py @@ -23,8 +23,9 @@ class Suite(object): print(''' SPEED = quick -.speed.quick = $(foreach s,$(sort $(filter-out %-slow, $1)), --suite $s) -.speed.slow = $(foreach s,$(sort $1), --suite $s) +.speed.quick = $(foreach s,$(sort $(filter-out %-slow %-thorough, $1)), --suite $s) +.speed.slow = $(foreach s,$(sort $(filter-out %-thorough, $1)), --suite $s) +.speed.thorough = $(foreach s,$(sort $1), --suite $s) .mtestargs = --no-rebuild -t 0 ifneq ($(SPEED), quick) @@ -52,11 +53,14 @@ def process_tests(test, targets, suites): for s in test_suites: # The suite name in the introspection info is "PROJECT:SUITE" s = s.split(':')[1] - if s == 'slow': + if s == 'slow' or s == 'thorough': continue if s.endswith('-slow'): s = s[:-5] suites[s].speeds.append('slow') + if s.endswith('-thorough'): + s = s[:-9] + suites[s].speeds.append('thorough') suites[s].deps.update(deps) def emit_prolog(suites, prefix): From 98487b9035d8540376024df74ab5510fdc37f12e Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 6 Oct 2021 11:27:47 +0200 Subject: [PATCH 027/460] build: make check-block a meson test "meson test" can be asked to run tests verbosely; this makes it usable also for qemu-iotests's own harness, and it lets "make check-block" reuse mtest2make.py's infrastructure to find and build test dependencies. Adjust check-block.sh to use the standard exit code that reports a test as skipped. Alternatively, in the future we could make it produce TAP output, which is consistent with all other "make check" tests. Signed-off-by: Paolo Bonzini --- meson.build | 6 +++--- scripts/mtest2make.py | 10 +++++++++- tests/Makefile.include | 16 +++------------- tests/check-block.sh | 28 +++++++++++++--------------- tests/meson.build | 1 + tests/qemu-iotests/meson.build | 29 +++++++++++++++++++++++++++++ 6 files changed, 58 insertions(+), 32 deletions(-) create mode 100644 tests/qemu-iotests/meson.build diff --git a/meson.build b/meson.build index f3f7b9c191..f025623bab 100644 --- a/meson.build +++ b/meson.build @@ -3,9 +3,9 @@ project('qemu', ['c'], meson_version: '>=0.58.2', 'b_staticpic=false', 'stdsplit=false'], version: files('VERSION')) -add_test_setup('quick', exclude_suites: ['slow', 'thorough'], is_default: true) -add_test_setup('slow', exclude_suites: ['thorough'], env: ['G_TEST_SLOW=1', 'SPEED=slow']) -add_test_setup('thorough', env: ['G_TEST_SLOW=1', 'SPEED=thorough']) +add_test_setup('quick', exclude_suites: ['block', 'slow', 'thorough'], is_default: true) +add_test_setup('slow', exclude_suites: ['block', 'thorough'], env: ['G_TEST_SLOW=1', 'SPEED=slow']) +add_test_setup('thorough', exclude_suites: ['block'], env: ['G_TEST_SLOW=1', 'SPEED=thorough']) not_found = dependency('', required: false) keyval = import('keyval') diff --git a/scripts/mtest2make.py b/scripts/mtest2make.py index 4b9c561b30..4d542e8aaa 100644 --- a/scripts/mtest2make.py +++ b/scripts/mtest2make.py @@ -79,7 +79,7 @@ def emit_prolog(suites, prefix): print(f'{prefix}-report.junit.xml $(all-{prefix}-xml): {prefix}-report%.junit.xml: run-ninja') print(f'\t$(MAKE) {prefix}$* MTESTARGS="$(MTESTARGS) --logbase {prefix}-report$*" && ln -f meson-logs/$@ .') -def emit_suite(name, suite, prefix): +def emit_suite_deps(name, suite, prefix): deps = ' '.join(suite.deps) targets = f'{prefix}-{name} {prefix}-report-{name}.junit.xml {prefix} {prefix}-report.junit.xml' print() @@ -87,6 +87,10 @@ def emit_suite(name, suite, prefix): print(f'ifneq ($(filter {prefix}-build {targets}, $(MAKECMDGOALS)),)') print(f'.{prefix}.build-suites += {name}') print(f'endif') + +def emit_suite(name, suite, prefix): + emit_suite_deps(name, suite, prefix) + targets = f'{prefix}-{name} {prefix}-report-{name}.junit.xml {prefix} {prefix}-report.junit.xml' print(f'ifneq ($(filter {targets}, $(MAKECMDGOALS)),)') print(f'.{prefix}.mtest-suites += ' + ' '.join(suite.names(name))) print(f'endif') @@ -97,6 +101,10 @@ targets = {t['id']: [os.path.relpath(f) for f in t['filename']] testsuites = defaultdict(Suite) for test in introspect['tests']: process_tests(test, targets, testsuites) +# HACK: check-block is a separate target so that it runs with --verbose; +# only write the dependencies +emit_suite_deps('block', testsuites['block'], 'check') +del testsuites['block'] emit_prolog(testsuites, 'check') for name, suite in testsuites.items(): emit_suite(name, suite, 'check') diff --git a/tests/Makefile.include b/tests/Makefile.include index 3aba622400..9157a57b1a 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -156,19 +156,9 @@ check: ifeq ($(CONFIG_TOOLS)$(CONFIG_POSIX),yy) check: check-block -export PYTHON - -ifneq ($(filter check check-block check-build, $(MAKECMDGOALS)),) -ninja-cmd-goals += \ - qemu-img$(EXESUF) \ - qemu-io$(EXESUF) \ - qemu-nbd$(EXESUF) \ - storage-daemon/qemu-storage-daemon$(EXESUF) \ - $(filter qemu-system-%, $(ninja-targets)) -endif - -check-block: $(SRC_PATH)/tests/check-block.sh run-ninja - @$< +check-block: run-ninja + $(if $(MAKE.n),,+)$(MESON) test $(MTESTARGS) $(.mtestargs) --verbose \ + --logbase iotestslog $(call .speed.$(SPEED), block block-slow block-thorough) endif check-build: run-ninja diff --git a/tests/check-block.sh b/tests/check-block.sh index d98d49ad63..9afeea5275 100755 --- a/tests/check-block.sh +++ b/tests/check-block.sh @@ -1,6 +1,6 @@ #!/bin/sh -# Honor the SPEED environment variable, just like we do it for the qtests. +# Honor the SPEED environment variable, just like we do it for "meson test" if [ "$SPEED" = "slow" ]; then format_list="raw qcow2" group= @@ -16,9 +16,13 @@ if [ "$#" -ne 0 ]; then format_list="$@" fi +skip() { + echo "$*" + exit 77 +} + if grep -q "CONFIG_GPROF=y" config-host.mak 2>/dev/null ; then - echo "GPROF is enabled ==> Not running the qemu-iotests." - exit 0 + skip "GPROF is enabled ==> Not running the qemu-iotests." fi # Disable tests with any sanitizer except for specific ones @@ -36,36 +40,30 @@ for j in ${ALLOWED_SANITIZE_FLAGS}; do done if echo ${SANITIZE_FLAGS} | grep -q "\-fsanitize" 2>/dev/null; then # Have a sanitize flag that is not allowed, stop - echo "Sanitizers are enabled ==> Not running the qemu-iotests." - exit 0 + skip "Sanitizers are enabled ==> Not running the qemu-iotests." fi if [ -z "$(find . -name 'qemu-system-*' -print)" ]; then - echo "No qemu-system binary available ==> Not running the qemu-iotests." - exit 0 + skip "No qemu-system binary available ==> Not running the qemu-iotests." fi if ! command -v bash >/dev/null 2>&1 ; then - echo "bash not available ==> Not running the qemu-iotests." - exit 0 + skip "bash not available ==> Not running the qemu-iotests." fi if LANG=C bash --version | grep -q 'GNU bash, version [123]' ; then - echo "bash version too old ==> Not running the qemu-iotests." - exit 0 + skip "bash version too old ==> Not running the qemu-iotests." fi if ! (sed --version | grep 'GNU sed') > /dev/null 2>&1 ; then if ! command -v gsed >/dev/null 2>&1; then - echo "GNU sed not available ==> Not running the qemu-iotests." - exit 0 + skip "GNU sed not available ==> Not running the qemu-iotests." fi else # Double-check that we're not using BusyBox' sed which says # that "This is not GNU sed version 4.0" ... if sed --version | grep -q 'not GNU sed' ; then - echo "BusyBox sed not supported ==> Not running the qemu-iotests." - exit 0 + skip "BusyBox sed not supported ==> Not running the qemu-iotests." fi fi diff --git a/tests/meson.build b/tests/meson.build index 3f3882748a..d5e168d714 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -1,6 +1,7 @@ py3 = import('python').find_installation() subdir('bench') +subdir('qemu-iotests') test_qapi_outputs = [ 'qapi-builtin-types.c', diff --git a/tests/qemu-iotests/meson.build b/tests/qemu-iotests/meson.build new file mode 100644 index 0000000000..3a9425d15c --- /dev/null +++ b/tests/qemu-iotests/meson.build @@ -0,0 +1,29 @@ +if have_tools and targetos != 'windows' + qemu_iotests_binaries = [qemu_img, qemu_io, qemu_nbd, qsd] + qemu_iotests_env = {'PYTHON': python.full_path()} + qemu_iotests_formats = { + 'qcow2': 'quick', + 'raw': 'slow', + 'qed': 'thorough', + 'vmdk': 'thorough', + 'vpc': 'thorough' + } + + foreach k, v : emulators + if k.startswith('qemu-system-') + qemu_iotests_binaries += v + endif + endforeach + foreach format, speed: qemu_iotests_formats + if speed == 'quick' + suites = 'block' + else + suites = ['block-' + speed, speed] + endif + test('qemu-iotests ' + format, sh, args: [files('../check-block.sh'), format], + depends: qemu_iotests_binaries, env: qemu_iotests_env, + suite: suites, + timeout: 0, + is_parallel: false) + endforeach +endif From 18c1cdd21d318adf2d02d90e25e9c04f33db76e8 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 27 Oct 2021 15:31:44 +0200 Subject: [PATCH 028/460] qemu-iotests: require at least an argument to check-block.sh This is anyway how check-block.sh is used in practice, and by removing the list of formats in the script we avoid duplication between meson.build and check-block.sh. Signed-off-by: Paolo Bonzini --- tests/check-block.sh | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/tests/check-block.sh b/tests/check-block.sh index 9afeea5275..88e02453d2 100755 --- a/tests/check-block.sh +++ b/tests/check-block.sh @@ -1,19 +1,16 @@ #!/bin/sh -# Honor the SPEED environment variable, just like we do it for "meson test" -if [ "$SPEED" = "slow" ]; then - format_list="raw qcow2" - group= -elif [ "$SPEED" = "thorough" ]; then - format_list="raw qcow2 qed vmdk vpc" - group= -else - format_list=qcow2 - group="-g auto" +if [ "$#" -eq 0 ]; then + echo "Usage: $0 fmt..." >&2 + exit 99 fi -if [ "$#" -ne 0 ]; then - format_list="$@" +# Honor the SPEED environment variable, just like we do it for "meson test" +format_list="$@" +if [ "$SPEED" = "slow" ] || [ "$SPEED" = "thorough" ]; then + group= +else + group="-g auto" fi skip() { From d316859f4e28c74ab8b618895d2a5e0a865d3cf1 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 7 Jan 2022 13:18:11 +0100 Subject: [PATCH 029/460] check-block: replace -makecheck with TAP output Let "meson test" take care of showing the results of the individual tests, consistently with other output from "make check V=1". Signed-off-by: Paolo Bonzini --- tests/check-block.sh | 6 ++-- tests/qemu-iotests/check | 6 ++-- tests/qemu-iotests/meson.build | 1 + tests/qemu-iotests/testenv.py | 30 +++++++++---------- tests/qemu-iotests/testrunner.py | 49 +++++++++++++++++--------------- 5 files changed, 48 insertions(+), 44 deletions(-) diff --git a/tests/check-block.sh b/tests/check-block.sh index 88e02453d2..720a46bc36 100755 --- a/tests/check-block.sh +++ b/tests/check-block.sh @@ -14,8 +14,8 @@ else fi skip() { - echo "$*" - exit 77 + echo "1..0 #SKIP $*" + exit 0 } if grep -q "CONFIG_GPROF=y" config-host.mak 2>/dev/null ; then @@ -79,7 +79,7 @@ JOBS=$(echo "$MAKEFLAGS" | sed -n 's/\(^\|.* \)-j\([0-9]\+\)\( .*\|$\)/-j \2/p') ret=0 for fmt in $format_list ; do - ${PYTHON} ./check $JOBS -makecheck -$fmt $group || ret=1 + ${PYTHON} ./check $JOBS -tap -$fmt $group || ret=1 done exit $ret diff --git a/tests/qemu-iotests/check b/tests/qemu-iotests/check index 0c27721a41..75de1b4691 100755 --- a/tests/qemu-iotests/check +++ b/tests/qemu-iotests/check @@ -32,8 +32,6 @@ def make_argparser() -> argparse.ArgumentParser: p.add_argument('-n', '--dry-run', action='store_true', help='show me, do not run tests') - p.add_argument('-makecheck', action='store_true', - help='pretty print output for make check') p.add_argument('-j', dest='jobs', type=int, default=1, help='run tests in multiple parallel jobs') @@ -53,6 +51,8 @@ def make_argparser() -> argparse.ArgumentParser: p.add_argument('--color', choices=['on', 'off', 'auto'], default='auto', help="use terminal colors. The default " "'auto' value means use colors if terminal stdout detected") + p.add_argument('-tap', action='store_true', + help='produce TAP output') g_env = p.add_argument_group('test environment options') mg = g_env.add_mutually_exclusive_group() @@ -164,7 +164,7 @@ if __name__ == '__main__': if args.dry_run: print('\n'.join(tests)) else: - with TestRunner(env, makecheck=args.makecheck, + with TestRunner(env, tap=args.tap, color=args.color) as tr: paths = [os.path.join(env.source_iotests, t) for t in tests] ok = tr.run_tests(paths, args.jobs) diff --git a/tests/qemu-iotests/meson.build b/tests/qemu-iotests/meson.build index 3a9425d15c..5be3c74127 100644 --- a/tests/qemu-iotests/meson.build +++ b/tests/qemu-iotests/meson.build @@ -22,6 +22,7 @@ if have_tools and targetos != 'windows' endif test('qemu-iotests ' + format, sh, args: [files('../check-block.sh'), format], depends: qemu_iotests_binaries, env: qemu_iotests_env, + protocol: 'tap', suite: suites, timeout: 0, is_parallel: false) diff --git a/tests/qemu-iotests/testenv.py b/tests/qemu-iotests/testenv.py index c33454fa68..0f32897fe8 100644 --- a/tests/qemu-iotests/testenv.py +++ b/tests/qemu-iotests/testenv.py @@ -287,21 +287,21 @@ class TestEnv(ContextManager['TestEnv']): def __exit__(self, exc_type: Any, exc_value: Any, traceback: Any) -> None: self.close() - def print_env(self) -> None: + def print_env(self, prefix: str = '') -> None: template = """\ -QEMU -- "{QEMU_PROG}" {QEMU_OPTIONS} -QEMU_IMG -- "{QEMU_IMG_PROG}" {QEMU_IMG_OPTIONS} -QEMU_IO -- "{QEMU_IO_PROG}" {QEMU_IO_OPTIONS} -QEMU_NBD -- "{QEMU_NBD_PROG}" {QEMU_NBD_OPTIONS} -IMGFMT -- {IMGFMT}{imgopts} -IMGPROTO -- {IMGPROTO} -PLATFORM -- {platform} -TEST_DIR -- {TEST_DIR} -SOCK_DIR -- {SOCK_DIR} -GDB_OPTIONS -- {GDB_OPTIONS} -VALGRIND_QEMU -- {VALGRIND_QEMU} -PRINT_QEMU_OUTPUT -- {PRINT_QEMU} -""" +{prefix}QEMU -- "{QEMU_PROG}" {QEMU_OPTIONS} +{prefix}QEMU_IMG -- "{QEMU_IMG_PROG}" {QEMU_IMG_OPTIONS} +{prefix}QEMU_IO -- "{QEMU_IO_PROG}" {QEMU_IO_OPTIONS} +{prefix}QEMU_NBD -- "{QEMU_NBD_PROG}" {QEMU_NBD_OPTIONS} +{prefix}IMGFMT -- {IMGFMT}{imgopts} +{prefix}IMGPROTO -- {IMGPROTO} +{prefix}PLATFORM -- {platform} +{prefix}TEST_DIR -- {TEST_DIR} +{prefix}SOCK_DIR -- {SOCK_DIR} +{prefix}GDB_OPTIONS -- {GDB_OPTIONS} +{prefix}VALGRIND_QEMU -- {VALGRIND_QEMU} +{prefix}PRINT_QEMU_OUTPUT -- {PRINT_QEMU} +{prefix}""" args = collections.defaultdict(str, self.get_env()) @@ -310,5 +310,5 @@ PRINT_QEMU_OUTPUT -- {PRINT_QEMU} u = os.uname() args['platform'] = f'{u.sysname}/{u.machine} {u.nodename} {u.release}' - + args['prefix'] = prefix print(template.format_map(args)) diff --git a/tests/qemu-iotests/testrunner.py b/tests/qemu-iotests/testrunner.py index 15788f919e..0eace147b8 100644 --- a/tests/qemu-iotests/testrunner.py +++ b/tests/qemu-iotests/testrunner.py @@ -152,10 +152,10 @@ class TestRunner(ContextManager['TestRunner']): return results - def __init__(self, env: TestEnv, makecheck: bool = False, + def __init__(self, env: TestEnv, tap: bool = False, color: str = 'auto') -> None: self.env = env - self.makecheck = makecheck + self.tap = tap self.last_elapsed = LastElapsedTime('.last-elapsed-cache', env) assert color in ('auto', 'on', 'off') @@ -185,13 +185,16 @@ class TestRunner(ContextManager['TestRunner']): """ Print short test info before/after test run """ test = os.path.basename(test) - if self.makecheck and status != '...': - if status and status != 'pass': - status = f' [{status}]' - else: - status = '' + if test_field_width is None: + test_field_width = 8 - print(f' TEST iotest-{self.env.imgfmt}: {test}{status}') + if self.tap: + if status == 'pass': + print(f'ok {self.env.imgfmt} {test}') + elif status == 'fail': + print(f'not ok {self.env.imgfmt} {test}') + elif status == 'not run': + print(f'ok {self.env.imgfmt} {test} # SKIP') return if lasttime: @@ -343,7 +346,7 @@ class TestRunner(ContextManager['TestRunner']): last_el = self.last_elapsed.get(test) start = datetime.datetime.now().strftime('%H:%M:%S') - if not self.makecheck: + if not self.tap: self.test_print_one_line(test=test, test_field_width=test_field_width, status = 'started' if mp else '...', @@ -372,7 +375,9 @@ class TestRunner(ContextManager['TestRunner']): notrun = [] casenotrun = [] - if not self.makecheck: + if self.tap: + self.env.print_env('# ') + else: self.env.print_env() test_field_width = max(len(os.path.basename(t)) for t in tests) + 2 @@ -398,8 +403,6 @@ class TestRunner(ContextManager['TestRunner']): if res.status == 'fail': failed.append(name) - if self.makecheck: - self.env.print_env() if res.diff: print('\n'.join(res.diff)) elif res.status == 'not run': @@ -412,16 +415,16 @@ class TestRunner(ContextManager['TestRunner']): if res.interrupted: break - if notrun: - print('Not run:', ' '.join(notrun)) + if not self.tap: + if notrun: + print('Not run:', ' '.join(notrun)) - if casenotrun: - print('Some cases not run in:', ' '.join(casenotrun)) + if casenotrun: + print('Some cases not run in:', ' '.join(casenotrun)) - if failed: - print('Failures:', ' '.join(failed)) - print(f'Failed {len(failed)} of {n_run} iotests') - return False - else: - print(f'Passed all {n_run} iotests') - return True + if failed: + print('Failures:', ' '.join(failed)) + print(f'Failed {len(failed)} of {n_run} iotests') + else: + print(f'Passed all {n_run} iotests') + return not failed From a66bd91f030827742778a9e0da19fe55716b4a60 Mon Sep 17 00:00:00 2001 From: Yang Zhong Date: Thu, 20 Jan 2022 17:31:04 -0500 Subject: [PATCH 030/460] qapi: Cleanup SGX related comments and restore @section-size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The SGX NUMA patches were merged into Qemu 7.0 release, we need clarify detailed version history information and also change some related comments, which make SGX related comments clearer. The QMP command schema promises backwards compatibility as standard. We temporarily restore "@section-size", which can avoid incompatible API breakage. The "@section-size" will be deprecated in 7.2 version. Suggested-by: Daniel P. Berrangé Signed-off-by: Yang Zhong Reviewed-by: Daniel P. Berrangé Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20220120223104.437161-1-yang.zhong@intel.com> Signed-off-by: Paolo Bonzini --- docs/about/deprecated.rst | 13 +++++++++++++ hw/i386/sgx.c | 11 +++++++++-- qapi/machine.json | 4 ++-- qapi/misc-target.json | 22 +++++++++++++++++----- 4 files changed, 41 insertions(+), 9 deletions(-) diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index e21e07478f..47a594a3b6 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -264,6 +264,19 @@ 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. +``query-sgx`` return value member ``section-size`` (since 7.0) +'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +Member ``section-size`` in return value elements with meta-type ``uint64`` is +deprecated. Use ``sections`` instead. + + +``query-sgx-capabilities`` return value member ``section-size`` (since 7.0) +''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +Member ``section-size`` in return value elements with meta-type ``uint64`` is +deprecated. Use ``sections`` instead. + System accelerators ------------------- diff --git a/hw/i386/sgx.c b/hw/i386/sgx.c index 5de5dd0893..a2b318dd93 100644 --- a/hw/i386/sgx.c +++ b/hw/i386/sgx.c @@ -83,7 +83,7 @@ static uint64_t sgx_calc_section_metric(uint64_t low, uint64_t high) ((high & MAKE_64BIT_MASK(0, 20)) << 32); } -static SGXEPCSectionList *sgx_calc_host_epc_sections(void) +static SGXEPCSectionList *sgx_calc_host_epc_sections(uint64_t *size) { SGXEPCSectionList *head = NULL, **tail = &head; SGXEPCSection *section; @@ -106,6 +106,7 @@ static SGXEPCSectionList *sgx_calc_host_epc_sections(void) section = g_new0(SGXEPCSection, 1); section->node = j++; section->size = sgx_calc_section_metric(ecx, edx); + *size += section->size; QAPI_LIST_APPEND(tail, section); } @@ -156,6 +157,7 @@ SGXInfo *qmp_query_sgx_capabilities(Error **errp) { SGXInfo *info = NULL; uint32_t eax, ebx, ecx, edx; + uint64_t size = 0; int fd = qemu_open_old("/dev/sgx_vepc", O_RDWR); if (fd < 0) { @@ -173,7 +175,8 @@ SGXInfo *qmp_query_sgx_capabilities(Error **errp) info->sgx1 = eax & (1U << 0) ? true : false; info->sgx2 = eax & (1U << 1) ? true : false; - info->sections = sgx_calc_host_epc_sections(); + info->sections = sgx_calc_host_epc_sections(&size); + info->section_size = size; close(fd); @@ -220,12 +223,14 @@ SGXInfo *qmp_query_sgx(Error **errp) return NULL; } + SGXEPCState *sgx_epc = &pcms->sgx_epc; info = g_new0(SGXInfo, 1); info->sgx = true; info->sgx1 = true; info->sgx2 = true; info->flc = true; + info->section_size = sgx_epc->size; info->sections = sgx_get_epc_sections_list(); return info; @@ -249,6 +254,8 @@ void hmp_info_sgx(Monitor *mon, const QDict *qdict) info->sgx2 ? "enabled" : "disabled"); monitor_printf(mon, "FLC support: %s\n", info->flc ? "enabled" : "disabled"); + monitor_printf(mon, "size: %" PRIu64 "\n", + info->section_size); section_list = info->sections; for (section = section_list; section; section = section->next) { diff --git a/qapi/machine.json b/qapi/machine.json index c87c81b803..42fc68403d 100644 --- a/qapi/machine.json +++ b/qapi/machine.json @@ -1207,7 +1207,7 @@ # # @memdev: memory backend linked with device # -# @node: the numa node +# @node: the numa node (Since: 7.0) # # Since: 6.2 ## @@ -1288,7 +1288,7 @@ # # @memdev: memory backend linked with device # -# @node: the numa node +# @node: the numa node (Since: 7.0) # # Since: 6.2 ## diff --git a/qapi/misc-target.json b/qapi/misc-target.json index 1022aa0184..4bc45d2474 100644 --- a/qapi/misc-target.json +++ b/qapi/misc-target.json @@ -344,9 +344,9 @@ # # @node: the numa node # -# @size: the size of epc section +# @size: the size of EPC section # -# Since: 6.2 +# Since: 7.0 ## { 'struct': 'SGXEPCSection', 'data': { 'node': 'int', @@ -365,7 +365,13 @@ # # @flc: true if FLC is supported # -# @sections: The EPC sections info for guest +# @section-size: The EPC section size for guest +# Redundant with @sections. Just for backward compatibility. +# +# @sections: The EPC sections info for guest (Since: 7.0) +# +# Features: +# @deprecated: Member @section-size is deprecated. Use @sections instead. # # Since: 6.2 ## @@ -374,6 +380,8 @@ 'sgx1': 'bool', 'sgx2': 'bool', 'flc': 'bool', + 'section-size': { 'type': 'uint64', + 'features': [ 'deprecated' ] }, 'sections': ['SGXEPCSection']}, 'if': 'TARGET_I386' } @@ -390,7 +398,9 @@ # # -> { "execute": "query-sgx" } # <- { "return": { "sgx": true, "sgx1" : true, "sgx2" : true, -# "flc": true, "section-size" : 0 } } +# "flc": true, "section-size" : 96468992, +# "sections": [{"node": 0, "size": 67108864}, +# {"node": 1, "size": 29360128}]} } # ## { 'command': 'query-sgx', 'returns': 'SGXInfo', 'if': 'TARGET_I386' } @@ -408,7 +418,9 @@ # # -> { "execute": "query-sgx-capabilities" } # <- { "return": { "sgx": true, "sgx1" : true, "sgx2" : true, -# "flc": true, "section-size" : 0 } } +# "flc": true, "section-size" : 96468992, +# "section" : [{"node": 0, "size": 67108864}, +# {"node": 1, "size": 29360128}]} } # ## { 'command': 'query-sgx-capabilities', 'returns': 'SGXInfo', 'if': 'TARGET_I386' } From 479ca4ccd54afcd54b0163532709079233d64b97 Mon Sep 17 00:00:00 2001 From: Matheus Ferst Date: Thu, 20 Jan 2022 14:31:41 -0300 Subject: [PATCH 031/460] configure: fix parameter expansion of --cross-cc-cflags options MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Without this fix, any use of --cross-cc-cflags-* causes a message like: $ ../configure --cross-cc-ppc64le=clang --cross-cc-cflags-ppc64le="-target powerpc64le-unknown-linux-gnu -sysroot ..." ../configure: 1: eval: cross_cc_cflags_--cross-cc-cflags-ppc64le=-target: not found ../configure: 3816: export: cross_cc_cflags_--cross-cc-cflags-ppc64le: bad variable name Signed-off-by: Matheus Ferst Message-Id: <20220120173142.2755077-1-matheus.ferst@eldorado.org.br> [Fix other occurrences too, noted by Philippe Mathieu-Daudé. - Paolo] Signed-off-by: Paolo Bonzini --- configure | 4 ++-- docs/devel/testing.rst | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/configure b/configure index e1a31fb332..e6cfc0e4be 100755 --- a/configure +++ b/configure @@ -402,7 +402,7 @@ for opt do ;; --cross-cc-*[!a-zA-Z0-9_-]*=*) error_exit "Passed bad --cross-cc-FOO option" ;; - --cross-cc-cflags-*) cc_arch=${opt#--cross-cc-flags-}; cc_arch=${cc_arch%%=*} + --cross-cc-cflags-*) cc_arch=${opt#--cross-cc-cflags-}; cc_arch=${cc_arch%%=*} eval "cross_cc_cflags_${cc_arch}=\$optarg" cross_cc_vars="$cross_cc_vars cross_cc_cflags_${cc_arch}" ;; @@ -1328,7 +1328,7 @@ Advanced options (experts only): --extra-cxxflags=CXXFLAGS append extra C++ compiler flags CXXFLAGS --extra-ldflags=LDFLAGS append extra linker flags LDFLAGS --cross-cc-ARCH=CC use compiler when building ARCH guest test cases - --cross-cc-flags-ARCH= use compiler flags when building ARCH guest tests + --cross-cc-cflags-ARCH= use compiler flags when building ARCH guest tests --make=MAKE use specified make [$make] --python=PYTHON use specified python [$python] --sphinx-build=SPHINX use specified sphinx-build [$sphinx_build] diff --git a/docs/devel/testing.rst b/docs/devel/testing.rst index d744b5909c..92d40cdd19 100644 --- a/docs/devel/testing.rst +++ b/docs/devel/testing.rst @@ -1324,7 +1324,7 @@ for the architecture in question, for example:: $(configure) --cross-cc-aarch64=aarch64-cc -There is also a ``--cross-cc-flags-ARCH`` flag in case additional +There is also a ``--cross-cc-cflags-ARCH`` flag in case additional compiler flags are needed to build for a given target. If you have the ability to run containers as the user the build system From 6e3f09c28a2e1767dddaf08b2f1414cd57c6c909 Mon Sep 17 00:00:00 2001 From: Alexey Kardashevskiy Date: Fri, 28 Jan 2022 13:15:01 +0100 Subject: [PATCH 032/460] spapr: Force 32bit when resetting a core MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "PowerPC Processor binding to IEEE 1275" says in "8.2.1. Initial Register Values" that the initial state is defined as 32bit so do it for both SLOF and VOF. This should not cause behavioral change as SLOF switches to 64bit very early anyway. As nothing enforces LE anywhere, this drops it for VOF. The goal is to make VOF work with TCG as otherwise it barfs with qemu: fatal: TCG hflags mismatch (current:0x6c000004 rebuilt:0x6c000000) Signed-off-by: Alexey Kardashevskiy Reviewed-by: Cédric Le Goater Message-Id: <20220107072423.2278113-1-aik@ozlabs.ru> Signed-off-by: Cédric Le Goater --- hw/ppc/spapr_cpu_core.c | 5 +++++ hw/ppc/spapr_vof.c | 2 -- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/hw/ppc/spapr_cpu_core.c b/hw/ppc/spapr_cpu_core.c index a57ba70a87..a781e97f8d 100644 --- a/hw/ppc/spapr_cpu_core.c +++ b/hw/ppc/spapr_cpu_core.c @@ -37,6 +37,11 @@ static void spapr_reset_vcpu(PowerPCCPU *cpu) cpu_reset(cs); + /* + * "PowerPC Processor binding to IEEE 1275" defines the initial MSR state + * as 32bit (MSR_SF=0) in "8.2.1. Initial Register Values". + */ + env->msr &= ~(1ULL << MSR_SF); env->spr[SPR_HIOR] = 0; lpcr = env->spr[SPR_LPCR]; diff --git a/hw/ppc/spapr_vof.c b/hw/ppc/spapr_vof.c index 40ce8fe003..a33f940c32 100644 --- a/hw/ppc/spapr_vof.c +++ b/hw/ppc/spapr_vof.c @@ -88,8 +88,6 @@ void spapr_vof_reset(SpaprMachineState *spapr, void *fdt, Error **errp) spapr_cpu_set_entry_state(first_ppc_cpu, SPAPR_ENTRY_POINT, stack_ptr, spapr->initrd_base, spapr->initrd_size); - /* VOF is 32bit BE so enforce MSR here */ - first_ppc_cpu->env.msr &= ~((1ULL << MSR_SF) | (1ULL << MSR_LE)); /* * At this point the expected allocation map is: From 5aad0457eceec0085a289dde72e73f15db556b99 Mon Sep 17 00:00:00 2001 From: Christophe Leroy Date: Fri, 28 Jan 2022 13:15:01 +0100 Subject: [PATCH 033/460] target/ppc: 603: fix restore of GPRs 0-3 on rfi MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After a TLB miss exception, GPRs 0-3 must be restored on rfi. This is managed by hreg_store_msr() which is called by do_rfi() However, hreg_store_msr() does it if MSR[TGPR] is unset in the passed MSR value. The problem is that do_rfi() is given the content of SRR1 as the value to be set in MSR, but TGPR bit is not part of SRR1 and that bit is used for something else and is sometimes set to 1, leading to hreg_store_msr() not restoring GPRs. So, do the same way as for POW bit, force clearing it. Signed-off-by: Christophe Leroy Cc: Cedric Le Goater Cc: Fabiano Rosas Reviewed-by: Cédric Le Goater Message-Id: <20220120103824.239573-1-christophe.leroy@csgroup.eu> Signed-off-by: Cédric Le Goater --- target/ppc/excp_helper.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index bc646c67a0..980f62fd79 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -1164,6 +1164,10 @@ static void do_rfi(CPUPPCState *env, target_ulong nip, target_ulong msr) /* MSR:POW cannot be set by any form of rfi */ msr &= ~(1ULL << MSR_POW); + /* MSR:TGPR cannot be set by any form of rfi */ + if (env->flags & POWERPC_FLAG_TGPR) + msr &= ~(1ULL << MSR_TGPR); + #if defined(TARGET_PPC64) /* Switching to 32-bit ? Crop the nip */ if (!msr_is_64bit(env, msr)) { From e31ea5d89b613072e64b83f67e9763e9b149b87e Mon Sep 17 00:00:00 2001 From: Vitaly Cheptsov Date: Fri, 28 Jan 2022 13:15:02 +0100 Subject: [PATCH 034/460] target/ppc/mmu_common: Fix SRR1/MSR error code on Book-E MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Book-E architecture does not set the error code in 31:27 bits of SRR1, but instead uses these bits for custom fields such as GS (Guest Supervisor). Wrongly setting these fields will result in QEMU crashes when attempting to execute not executable code due to the attempts to use Guest Supervisor mode. Cc: "Cédric Le Goater" Cc: Daniel Henrique Barboza Cc: David Gibson Cc: Greg Kurz Cc: qemu-ppc@nongnu.org Cc: qemu-devel@nongnu.org Cc: qemu-stable@nongnu.org Signed-off-by: Vitaly Cheptsov Reviewed-by: Cédric Le Goater Message-Id: <20220121093107.15478-1-cheptsov@ispras.ru> Signed-off-by: Cédric Le Goater --- target/ppc/mmu_common.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index 91270c1f17..6512ee031c 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -1367,22 +1367,34 @@ static bool ppc_jumbo_xlate(PowerPCCPU *cpu, vaddr eaddr, case -2: /* Access rights violation */ cs->exception_index = POWERPC_EXCP_ISI; - env->error_code = 0x08000000; + 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; - env->error_code = 0x10000000; break; case -4: /* Direct store exception */ /* No code fetch is allowed in direct-store areas */ cs->exception_index = POWERPC_EXCP_ISI; - env->error_code = 0x10000000; + if ((env->mmu_model == POWERPC_MMU_BOOKE) || + (env->mmu_model == POWERPC_MMU_BOOKE206)) { + env->error_code = 0; + } else { + env->error_code = 0x10000000; + } break; } } else { From 50c8e11ac0672b726a2b3e2217cb32dc8416299f Mon Sep 17 00:00:00 2001 From: Frederic Barrat Date: Fri, 28 Jan 2022 13:15:02 +0100 Subject: [PATCH 035/460] ppc/pnv: Fail DMA access if page permissions are not correct MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If an iommu page has wrong permissions, an error message is displayed, but the access is allowed, which is odd. This patch fixes it. Signed-off-by: Frederic Barrat Reviewed-by: Daniel Henrique Barboza Message-Id: <20220121152350.381685-1-fbarrat@linux.ibm.com> Signed-off-by: Cédric Le Goater --- hw/pci-host/pnv_phb3.c | 11 ++++++----- hw/pci-host/pnv_phb4.c | 11 ++++++----- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/hw/pci-host/pnv_phb3.c b/hw/pci-host/pnv_phb3.c index 7fb35dc031..a757f1a58e 100644 --- a/hw/pci-host/pnv_phb3.c +++ b/hw/pci-host/pnv_phb3.c @@ -816,18 +816,19 @@ static void pnv_phb3_translate_tve(PnvPhb3DMASpace *ds, hwaddr addr, } /* We exit the loop with TCE being the final TCE */ - tce_mask = ~((1ull << tce_shift) - 1); - tlb->iova = addr & tce_mask; - tlb->translated_addr = tce & tce_mask; - tlb->addr_mask = ~tce_mask; - tlb->perm = tce & 3; if ((is_write & !(tce & 2)) || ((!is_write) && !(tce & 1))) { phb3_error(phb, "TCE access fault at 0x%"PRIx64, taddr); phb3_error(phb, " xlate %"PRIx64":%c TVE=%"PRIx64, addr, is_write ? 'W' : 'R', tve); phb3_error(phb, " tta=%"PRIx64" lev=%d tts=%d tps=%d", tta, lev, tts, tps); + return; } + tce_mask = ~((1ull << tce_shift) - 1); + tlb->iova = addr & tce_mask; + tlb->translated_addr = tce & tce_mask; + tlb->addr_mask = ~tce_mask; + tlb->perm = tce & 3; } } diff --git a/hw/pci-host/pnv_phb4.c b/hw/pci-host/pnv_phb4.c index a78add75b0..ee56377c02 100644 --- a/hw/pci-host/pnv_phb4.c +++ b/hw/pci-host/pnv_phb4.c @@ -1291,18 +1291,19 @@ static void pnv_phb4_translate_tve(PnvPhb4DMASpace *ds, hwaddr addr, } /* We exit the loop with TCE being the final TCE */ - tce_mask = ~((1ull << tce_shift) - 1); - tlb->iova = addr & tce_mask; - tlb->translated_addr = tce & tce_mask; - tlb->addr_mask = ~tce_mask; - tlb->perm = tce & 3; if ((is_write & !(tce & 2)) || ((!is_write) && !(tce & 1))) { phb_error(ds->phb, "TCE access fault at 0x%"PRIx64, taddr); phb_error(ds->phb, " xlate %"PRIx64":%c TVE=%"PRIx64, addr, is_write ? 'W' : 'R', tve); phb_error(ds->phb, " tta=%"PRIx64" lev=%d tts=%d tps=%d", tta, lev, tts, tps); + return; } + tce_mask = ~((1ull << tce_shift) - 1); + tlb->iova = addr & tce_mask; + tlb->translated_addr = tce & tce_mask; + tlb->addr_mask = ~tce_mask; + tlb->perm = tce & 3; } } From 83d2bea68a778b98ecbf9472be6f1ed8031719ac Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Fri, 28 Jan 2022 13:15:02 +0100 Subject: [PATCH 036/460] ppc/pnv: use a do-while() loop in pnv_phb3_translate_tve() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 'taddr' variable is left unintialized, being set only inside the "while ((lev--) >= 0)" loop where we get the TCE address. The 'lev' var is an int32_t that is being initiliazed by the GETFIELD() macro, which returns an uint64_t. For a human reader this means that 'lev' will always be positive or zero. But some compilers may beg to differ. 'lev' being an int32_t can in theory be set as negative, and the "while ((lev--) >= 0)" loop might never be reached, and 'taddr' will be left unitialized. This can cause phb3_error() to use 'taddr' uninitialized down below: if ((is_write & !(tce & 2)) || ((!is_write) && !(tce & 1))) { phb3_error(phb, "TCE access fault at 0x%"PRIx64, taddr); A quick way of fixing it is to use a do/while() loop. This will keep the same semanting as the existing while() loop does and the compiler will understand that 'taddr' will be initialized at least once. Suggested-by: Matheus K. Ferst Resolves: https://gitlab.com/qemu-project/qemu/-/issues/573 Signed-off-by: Daniel Henrique Barboza Message-Id: <20220127122234.842145-2-danielhb413@gmail.com> Signed-off-by: Cédric Le Goater --- hw/pci-host/pnv_phb3.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/hw/pci-host/pnv_phb3.c b/hw/pci-host/pnv_phb3.c index a757f1a58e..aafd46b635 100644 --- a/hw/pci-host/pnv_phb3.c +++ b/hw/pci-host/pnv_phb3.c @@ -792,7 +792,9 @@ static void pnv_phb3_translate_tve(PnvPhb3DMASpace *ds, hwaddr addr, sh = tbl_shift * lev + tce_shift; /* TODO: Multi-level untested */ - while ((lev--) >= 0) { + do { + lev--; + /* Grab the TCE address */ taddr = base | (((addr >> sh) & ((1ul << tbl_shift) - 1)) << 3); if (dma_memory_read(&address_space_memory, taddr, &tce, @@ -813,7 +815,7 @@ static void pnv_phb3_translate_tve(PnvPhb3DMASpace *ds, hwaddr addr, } sh -= tbl_shift; base = tce & ~0xfffull; - } + } while (lev >= 0); /* We exit the loop with TCE being the final TCE */ if ((is_write & !(tce & 2)) || ((!is_write) && !(tce & 1))) { From 799c179ed844b01542e729405c0bf5ef4cd294dc Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Fri, 28 Jan 2022 13:15:02 +0100 Subject: [PATCH 037/460] ppc/pnv: use a do-while() loop in pnv_phb4_translate_tve() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pnv_phb4_translate_tve() is quite similar to pnv_phb3_translate_tve(), and that includes the fact that 'taddr' can be considered uninitialized when throwing the "TCE access fault" error because, in theory, the loop that sets 'taddr' can be skippable due to 'lev' being an signed int. No one complained about this specific case yet, but since we took the time to handle the same situtation in pnv_phb3_translate_tve(), let's replicate it here as well. Signed-off-by: Daniel Henrique Barboza Reviewed-by: Matheus Ferst Message-Id: <20220127122234.842145-3-danielhb413@gmail.com> Signed-off-by: Cédric Le Goater --- hw/pci-host/pnv_phb4.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/hw/pci-host/pnv_phb4.c b/hw/pci-host/pnv_phb4.c index ee56377c02..e91249ef64 100644 --- a/hw/pci-host/pnv_phb4.c +++ b/hw/pci-host/pnv_phb4.c @@ -1267,7 +1267,9 @@ static void pnv_phb4_translate_tve(PnvPhb4DMASpace *ds, hwaddr addr, /* TODO: Limit to support IO page sizes */ /* TODO: Multi-level untested */ - while ((lev--) >= 0) { + do { + lev--; + /* Grab the TCE address */ taddr = base | (((addr >> sh) & ((1ul << tbl_shift) - 1)) << 3); if (dma_memory_read(&address_space_memory, taddr, &tce, @@ -1288,7 +1290,7 @@ static void pnv_phb4_translate_tve(PnvPhb4DMASpace *ds, hwaddr addr, } sh -= tbl_shift; base = tce & ~0xfffull; - } + } while (lev >= 0); /* We exit the loop with TCE being the final TCE */ if ((is_write & !(tce & 2)) || ((!is_write) && !(tce & 1))) { From e9711c6149fc087f5cadd6f60e9ffa5541e421af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Fri, 28 Jan 2022 13:15:02 +0100 Subject: [PATCH 038/460] ppc/xive: check return value of ldq_be_dma() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ldq_be_dma() routine was recently changed to return a result of the transaction. Use it when loading the virtual structure descriptors in the XIVE PowerNV model. Cc: Philippe Mathieu-Daudé Signed-off-by: Cédric Le Goater Message-Id: <20220124081635.3672439-1-clg@kaod.org> Signed-off-by: Cédric Le Goater --- hw/intc/pnv_xive.c | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/hw/intc/pnv_xive.c b/hw/intc/pnv_xive.c index bb207514f2..621b20a03f 100644 --- a/hw/intc/pnv_xive.c +++ b/hw/intc/pnv_xive.c @@ -172,7 +172,12 @@ static uint64_t pnv_xive_vst_addr_indirect(PnvXive *xive, uint32_t type, /* Get the page size of the indirect table. */ vsd_addr = vsd & VSD_ADDRESS_MASK; - ldq_be_dma(&address_space_memory, vsd_addr, &vsd, MEMTXATTRS_UNSPECIFIED); + if (ldq_be_dma(&address_space_memory, vsd_addr, &vsd, + MEMTXATTRS_UNSPECIFIED)) { + xive_error(xive, "VST: failed to access %s entry %x @0x%" PRIx64, + info->name, idx, vsd_addr); + return 0; + } if (!(vsd & VSD_ADDRESS_MASK)) { #ifdef XIVE_DEBUG @@ -195,8 +200,12 @@ static uint64_t pnv_xive_vst_addr_indirect(PnvXive *xive, uint32_t type, /* Load the VSD we are looking for, if not already done */ if (vsd_idx) { vsd_addr = vsd_addr + vsd_idx * XIVE_VSD_SIZE; - ldq_be_dma(&address_space_memory, vsd_addr, &vsd, - MEMTXATTRS_UNSPECIFIED); + if (ldq_be_dma(&address_space_memory, vsd_addr, &vsd, + MEMTXATTRS_UNSPECIFIED)) { + xive_error(xive, "VST: failed to access %s entry %x @0x%" + PRIx64, info->name, vsd_idx, vsd_addr); + return 0; + } if (!(vsd & VSD_ADDRESS_MASK)) { #ifdef XIVE_DEBUG @@ -543,7 +552,12 @@ static uint64_t pnv_xive_vst_per_subpage(PnvXive *xive, uint32_t type) /* Get the page size of the indirect table. */ vsd_addr = vsd & VSD_ADDRESS_MASK; - ldq_be_dma(&address_space_memory, vsd_addr, &vsd, MEMTXATTRS_UNSPECIFIED); + if (ldq_be_dma(&address_space_memory, vsd_addr, &vsd, + MEMTXATTRS_UNSPECIFIED)) { + xive_error(xive, "VST: failed to access %s entry @0x%" PRIx64, + info->name, vsd_addr); + return 0; + } if (!(vsd & VSD_ADDRESS_MASK)) { #ifdef XIVE_DEBUG From 0c0aac01c49cc159a37841b1954b1938f0582fb4 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Fri, 28 Jan 2022 13:15:02 +0100 Subject: [PATCH 039/460] target/ppc: fix 'skip KVM' cond in cpu_interrupt_exittb() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit cpu_interrupt_exittb() was introduced by commit 044897ef4a22 ("target/ppc: Fix system lockups caused by interrupt_request state corruption") as a way to wrap cpu_interrupt() helper in BQL. After that, commit 6d38666a8931 ("ppc: Ignore the CPU_INTERRUPT_EXITTB interrupt with KVM") added a condition to skip this interrupt if we're running with KVM. Problem is that the change made by the above commit, testing for !kvm_enabled() at the start of cpu_interrupt_exittb(): static inline void cpu_interrupt_exittb(CPUState *cs) { if (!kvm_enabled()) { return; } (... do cpu_interrupt(cs, CPU_INTERRUPT_EXITTB) ...) is doing the opposite of what it intended to do. This will return immediately if not kvm_enabled(), i.e. it's a emulated CPU, and if kvm_enabled() it will proceed to fire CPU_INTERRUPT_EXITTB. Fix the 'skip KVM' condition so the function is a no-op when kvm_enabled(). CC: Greg Kurz Resolves: https://gitlab.com/qemu-project/qemu/-/issues/809 Fixes: 6d38666a8931 ("ppc: Ignore the CPU_INTERRUPT_EXITTB interrupt with KVM") Signed-off-by: Daniel Henrique Barboza Reviewed-by: Fabiano Rosas Reviewed-by: Greg Kurz Message-Id: <20220121160841.9102-1-danielhb413@gmail.com> Signed-off-by: Cédric Le Goater --- target/ppc/helper_regs.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/target/ppc/helper_regs.c b/target/ppc/helper_regs.c index 8671b7bb69..7dca585ddd 100644 --- a/target/ppc/helper_regs.c +++ b/target/ppc/helper_regs.c @@ -201,7 +201,11 @@ void cpu_get_tb_cpu_state(CPUPPCState *env, target_ulong *pc, void cpu_interrupt_exittb(CPUState *cs) { - if (!kvm_enabled()) { + /* + * We don't need to worry about translation blocks + * when running with KVM. + */ + if (kvm_enabled()) { return; } From 1977434bbfbdd97d28c2fea071ea00bf4ecd0079 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Fri, 28 Jan 2022 13:15:02 +0100 Subject: [PATCH 040/460] spapr.c: check bus != NULL in spapr_get_fw_dev_path() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit spapr_get_fw_dev_path() is an impl of FWPathProviderClass::get_dev_path(). This interface is used by hw/core/qdev-fw.c via fw_path_provider_try_get_dev_path() in two functions: - static char *qdev_get_fw_dev_path_from_handler(), which is used only in qdev_get_fw_dev_path_helper() and it's guarded by "if (dev && dev->parent_bus)"; - char *qdev_get_own_fw_dev_path_from_handler(), which is used in softmmu/bootdevice.c in get_boot_device_path() like this: if (dev) { d = qdev_get_own_fw_dev_path_from_handler(dev->parent_bus, dev); This means that, when called via softmmu/bootdevice.c, there's no check of 'dev->parent_bus' being not NULL. The result is that the "BusState *bus" arg of spapr_get_fw_dev_path() can potentially be NULL and if, at the same time, "SCSIDevice *d" is not NULL, we'll hit this line: void *spapr = CAST(void, bus->parent, "spapr-vscsi"); And we'll SIGINT because 'bus' is NULL and we're accessing bus->parent. Adding a simple 'bus != NULL' check to guard the instances where we access 'bus->parent' can avoid this altogether. Signed-off-by: Daniel Henrique Barboza Reviewed-by: Cédric Le Goater Message-Id: <20220121213852.30243-1-danielhb413@gmail.com> Signed-off-by: Cédric Le Goater --- hw/ppc/spapr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 72f5dce751..3d6ec309dd 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -3053,7 +3053,7 @@ static char *spapr_get_fw_dev_path(FWPathProvider *p, BusState *bus, VHostSCSICommon *vsc = CAST(VHostSCSICommon, dev, TYPE_VHOST_SCSI_COMMON); PCIDevice *pcidev = CAST(PCIDevice, dev, TYPE_PCI_DEVICE); - if (d) { + if (d && bus) { void *spapr = CAST(void, bus->parent, "spapr-vscsi"); VirtIOSCSI *virtio = CAST(VirtIOSCSI, bus->parent, TYPE_VIRTIO_SCSI); USBDevice *usb = CAST(USBDevice, bus->parent, TYPE_USB_DEVICE); From 63f38cc3d228a0886b544e9a069b922b4416d29d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Fri, 28 Jan 2022 13:15:03 +0100 Subject: [PATCH 041/460] target/ppc: Fix test on mmu_model in hreg_compute_hflags_value() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit POWERPC_MMU_BOOKE is not a mask and should not be tested with a bitwise AND operator. It went unnoticed because it only impacts the 601 CPU implementation for which we don't have a known firmware image. Signed-off-by: Cédric Le Goater Reviewed-by: David Gibson Message-Id: <20220124081609.3672341-1-clg@kaod.org> Signed-off-by: Cédric Le Goater --- target/ppc/helper_regs.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/target/ppc/helper_regs.c b/target/ppc/helper_regs.c index 7dca585ddd..5b12cb03c9 100644 --- a/target/ppc/helper_regs.c +++ b/target/ppc/helper_regs.c @@ -156,7 +156,8 @@ static uint32_t hreg_compute_hflags_value(CPUPPCState *env) */ unsigned immu_idx, dmmu_idx; dmmu_idx = msr & (1 << MSR_PR) ? 0 : 1; - if (env->mmu_model & POWERPC_MMU_BOOKE) { + if (env->mmu_model == POWERPC_MMU_BOOKE || + env->mmu_model == POWERPC_MMU_BOOKE206) { dmmu_idx |= msr & (1 << MSR_GS) ? 4 : 0; immu_idx = dmmu_idx; immu_idx |= msr & (1 << MSR_IS) ? 2 : 0; @@ -237,7 +238,8 @@ int hreg_store_msr(CPUPPCState *env, target_ulong value, int alter_hv) ((value >> MSR_DR) & 1) != msr_dr) { cpu_interrupt_exittb(cs); } - if ((env->mmu_model & POWERPC_MMU_BOOKE) && + if ((env->mmu_model == POWERPC_MMU_BOOKE || + env->mmu_model == POWERPC_MMU_BOOKE206) && ((value >> MSR_GS) & 1) != msr_gs) { cpu_interrupt_exittb(cs); } From a01b64cee77061118b8cdaefeacd5440ec26107c Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Fri, 28 Jan 2022 13:15:03 +0100 Subject: [PATCH 042/460] target/ppc: Put do_rfi under a TCG-only block MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The --disable-tcg build broke when do_rfi stopped being inlined. Fixes: 62e79ef914 ("target/ppc: Remove static inline") Signed-off-by: Fabiano Rosas Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Daniel Henrique Barboza Message-Id: <20220124191547.1008391-1-farosas@linux.ibm.com> Signed-off-by: Cédric Le Goater --- target/ppc/excp_helper.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index 980f62fd79..883fb13cfe 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -1155,7 +1155,6 @@ void helper_pminsn(CPUPPCState *env, powerpc_pm_insn_t insn) (env->spr[SPR_PSSCR] & PSSCR_EC); } #endif /* defined(TARGET_PPC64) */ -#endif /* CONFIG_TCG */ static void do_rfi(CPUPPCState *env, target_ulong nip, target_ulong msr) { @@ -1192,7 +1191,6 @@ static void do_rfi(CPUPPCState *env, target_ulong nip, target_ulong msr) check_tlb_flush(env, false); } -#ifdef CONFIG_TCG void helper_rfi(CPUPPCState *env) { do_rfi(env, env->spr[SPR_SRR0], env->spr[SPR_SRR1] & 0xfffffffful); From dc10da64e1f704bec8ed66f6a402d22589a3c4f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 28 Jan 2022 13:15:03 +0100 Subject: [PATCH 043/460] hw/ppc/vof: Add missing includes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit vof.h requires "qom/object.h" for DECLARE_CLASS_CHECKERS(), "exec/memory.h" for address_space_read/write(), "exec/address-spaces.h" for address_space_memory and more importantly "cpu.h" for target_ulong. vof.c doesn't need "exec/ram_addr.h". Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20220122003104.84391-1-f4bug@amsat.org> Signed-off-by: Cédric Le Goater --- hw/ppc/vof.c | 1 - include/hw/ppc/vof.h | 5 +++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/hw/ppc/vof.c b/hw/ppc/vof.c index 73adc44ec2..2b63a62875 100644 --- a/hw/ppc/vof.c +++ b/hw/ppc/vof.c @@ -16,7 +16,6 @@ #include "qemu/units.h" #include "qemu/log.h" #include "qapi/error.h" -#include "exec/ram_addr.h" #include "exec/address-spaces.h" #include "hw/ppc/vof.h" #include "hw/ppc/fdt.h" diff --git a/include/hw/ppc/vof.h b/include/hw/ppc/vof.h index 97fdef758b..f8c0effcaf 100644 --- a/include/hw/ppc/vof.h +++ b/include/hw/ppc/vof.h @@ -6,6 +6,11 @@ #ifndef HW_VOF_H #define HW_VOF_H +#include "qom/object.h" +#include "exec/address-spaces.h" +#include "exec/memory.h" +#include "cpu.h" + typedef struct Vof { uint64_t top_addr; /* copied from rma_size */ GArray *claimed; /* array of SpaprOfClaimed */ From 47822486f5e7d6dad8d9a2381d127a831a3c5c11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Fri, 28 Jan 2022 13:15:03 +0100 Subject: [PATCH 044/460] ppc/ppc405: Fix TLB flushing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit cd0c6f473532 did not take into account 405 CPUs when adding support to batching of TCG tlb flushes. Set the TLB_NEED_LOCAL_FLUSH flag when the SPR_40x_PID is set or a TLB updated. Cc: Thomas Huth Cc: Christophe Leroy Cc: Fabiano Rosas Reviewed-by: Fabiano Rosas Fixes: cd0c6f473532 ("ppc: Do some batching of TCG tlb flushes") Signed-off-by: Cédric Le Goater Message-Id: <20220113180352.1234512-1-clg@kaod.org> Signed-off-by: Cédric Le Goater --- target/ppc/helper.h | 1 + target/ppc/mmu_helper.c | 12 +++++++++++- target/ppc/translate.c | 2 +- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/target/ppc/helper.h b/target/ppc/helper.h index d318837ea5..bdbbd5e1d9 100644 --- a/target/ppc/helper.h +++ b/target/ppc/helper.h @@ -707,6 +707,7 @@ DEF_HELPER_FLAGS_1(load_40x_pit, TCG_CALL_NO_RWG, tl, env) DEF_HELPER_FLAGS_2(store_40x_pit, TCG_CALL_NO_RWG, void, env, tl) DEF_HELPER_FLAGS_2(store_40x_tcr, TCG_CALL_NO_RWG, void, env, tl) DEF_HELPER_FLAGS_2(store_40x_tsr, TCG_CALL_NO_RWG, void, env, tl) +DEF_HELPER_2(store_40x_pid, void, env, tl) DEF_HELPER_2(store_40x_dbcr0, void, env, tl) DEF_HELPER_2(store_40x_sler, void, env, tl) DEF_HELPER_FLAGS_2(store_booke_tcr, TCG_CALL_NO_RWG, void, env, tl) diff --git a/target/ppc/mmu_helper.c b/target/ppc/mmu_helper.c index 59df6952ae..a2a52a12c3 100644 --- a/target/ppc/mmu_helper.c +++ b/target/ppc/mmu_helper.c @@ -664,6 +664,14 @@ static inline int booke_page_size_to_tlb(target_ulong page_size) #define PPC4XX_TLBLO_ATTR_MASK 0x000000FF #define PPC4XX_TLBLO_RPN_MASK 0xFFFFFC00 +void helper_store_40x_pid(CPUPPCState *env, target_ulong val) +{ + if (env->spr[SPR_40x_PID] != val) { + env->spr[SPR_40x_PID] = val; + env->tlb_need_flush |= TLB_NEED_LOCAL_FLUSH; + } +} + target_ulong helper_4xx_tlbre_hi(CPUPPCState *env, target_ulong entry) { ppcemb_tlb_t *tlb; @@ -681,7 +689,7 @@ target_ulong helper_4xx_tlbre_hi(CPUPPCState *env, target_ulong entry) size = PPC4XX_TLBHI_SIZE_DEFAULT; } ret |= size << PPC4XX_TLBHI_SIZE_SHIFT; - env->spr[SPR_40x_PID] = tlb->PID; + helper_store_40x_pid(env, tlb->PID); return ret; } @@ -794,6 +802,8 @@ void helper_4xx_tlbwe_lo(CPUPPCState *env, target_ulong entry, tlb->prot & PAGE_WRITE ? 'w' : '-', tlb->prot & PAGE_EXEC ? 'x' : '-', tlb->prot & PAGE_VALID ? 'v' : '-', (int)tlb->PID); + + env->tlb_need_flush |= TLB_NEED_LOCAL_FLUSH; } target_ulong helper_4xx_tlbsx(CPUPPCState *env, target_ulong address) diff --git a/target/ppc/translate.c b/target/ppc/translate.c index 9d2adc0cae..d61c6f0e8f 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -894,7 +894,7 @@ void spr_write_40x_pid(DisasContext *ctx, int sprn, int gprn) { TCGv t0 = tcg_temp_new(); tcg_gen_andi_tl(t0, cpu_gpr[gprn], 0xFF); - gen_store_spr(SPR_40x_PID, t0); + gen_helper_store_40x_pid(cpu_env, t0); tcg_temp_free(t0); } From 645d843ca55f0a7aa9be3ef19694d5a44b002f6e Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Fri, 28 Jan 2022 13:15:03 +0100 Subject: [PATCH 045/460] target/ppc: 405: Rename MSR_POW to MSR_WE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bit 13 is the Wait State Enable bit. Give it its proper name. As far as I can see we don't do anything with MSR_POW for the 405, so this change has no effect. Suggested-by: David Gibson Signed-off-by: Fabiano Rosas Reviewed-by: Cédric Le Goater Message-Id: <20220118184448.852996-2-farosas@linux.ibm.com> Signed-off-by: Cédric Le Goater --- target/ppc/cpu.h | 1 + target/ppc/cpu_init.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index 2560b70c5f..66e13075c3 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -327,6 +327,7 @@ typedef enum { #define MSR_S 22 /* Secure state */ #define MSR_KEY 19 /* key bit on 603e */ #define MSR_POW 18 /* Power management */ +#define MSR_WE 18 /* Wait State Enable on 405 */ #define MSR_TGPR 17 /* TGPR usage on 602/603 x */ #define MSR_CE 17 /* Critical interrupt enable on embedded PowerPC x */ #define MSR_ILE 16 /* Interrupt little-endian mode */ diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index e30e86fe9d..e63705b1c6 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -2535,7 +2535,7 @@ POWERPC_FAMILY(405)(ObjectClass *oc, void *data) PPC_MEM_SYNC | PPC_MEM_EIEIO | PPC_40x_TLB | PPC_MEM_TLBIA | PPC_MEM_TLBSYNC | PPC_4xx_COMMON | PPC_405_MAC | PPC_40x_EXCP; - pcc->msr_mask = (1ull << MSR_POW) | + pcc->msr_mask = (1ull << MSR_WE) | (1ull << MSR_CE) | (1ull << MSR_EE) | (1ull << MSR_PR) | From 301e5d48b15d2846cabe0f5c7600860b35c58c12 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Fri, 28 Jan 2022 13:15:03 +0100 Subject: [PATCH 046/460] target/ppc: 405: Add missing MSR_ME bit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 405 MSR has the Machine Check Enable bit. We're making use of it when dispatching Machine Check, so add the bit to the msr_mask. Signed-off-by: Fabiano Rosas Reviewed-by: Cédric Le Goater Message-Id: <20220118184448.852996-3-farosas@linux.ibm.com> Signed-off-by: Cédric Le Goater --- target/ppc/cpu_init.c | 1 + 1 file changed, 1 insertion(+) diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index e63705b1c6..23a13036b2 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -2540,6 +2540,7 @@ POWERPC_FAMILY(405)(ObjectClass *oc, void *data) (1ull << MSR_EE) | (1ull << MSR_PR) | (1ull << MSR_FP) | + (1ull << MSR_ME) | (1ull << MSR_DWE) | (1ull << MSR_DE) | (1ull << MSR_IR) | From e808c2ed07f25b93ed796dfe266a30f2236ad69a Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Fri, 28 Jan 2022 13:15:04 +0100 Subject: [PATCH 047/460] target/ppc: Introduce powerpc_excp_40x MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce a new powerpc_excp function specific for 40x CPUs. This commit copies powerpc_excp_legacy verbatim so the next one has a clean diff. Signed-off-by: Fabiano Rosas Reviewed-by: Cédric Le Goater Reviewed-by: Richard Henderson Message-Id: <20220118184448.852996-4-farosas@linux.ibm.com> Signed-off-by: Cédric Le Goater --- target/ppc/excp_helper.c | 474 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 474 insertions(+) diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index 883fb13cfe..5dc948aec2 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -392,6 +392,477 @@ static void powerpc_set_excp_state(PowerPCCPU *cpu, check_tlb_flush(env, false); } +static void powerpc_excp_40x(PowerPCCPU *cpu, int excp) +{ + CPUState *cs = CPU(cpu); + CPUPPCState *env = &cpu->env; + int excp_model = env->excp_model; + target_ulong msr, new_msr, vector; + int srr0, srr1, lev = -1; + + if (excp <= POWERPC_EXCP_NONE || excp >= POWERPC_EXCP_NB) { + cpu_abort(cs, "Invalid PowerPC exception %d. Aborting\n", excp); + } + + qemu_log_mask(CPU_LOG_INT, "Raise exception at " TARGET_FMT_lx + " => %s (%d) error=%02x\n", env->nip, powerpc_excp_name(excp), + excp, env->error_code); + + /* new srr1 value excluding must-be-zero bits */ + if (excp_model == POWERPC_EXCP_BOOKE) { + msr = env->msr; + } else { + msr = env->msr & ~0x783f0000ULL; + } + + /* + * new interrupt handler msr preserves existing HV and ME unless + * explicitly overriden + */ + new_msr = env->msr & (((target_ulong)1 << MSR_ME) | MSR_HVB); + + /* target registers */ + srr0 = SPR_SRR0; + srr1 = SPR_SRR1; + + /* + * check for special resume at 0x100 from doze/nap/sleep/winkle on + * P7/P8/P9 + */ + if (env->resume_as_sreset) { + excp = powerpc_reset_wakeup(cs, env, excp, &msr); + } + + /* + * Hypervisor emulation assistance interrupt only exists on server + * arch 2.05 server or later. We also don't want to generate it if + * we don't have HVB in msr_mask (PAPR mode). + */ + if (excp == POWERPC_EXCP_HV_EMU +#if defined(TARGET_PPC64) + && !(mmu_is_64bit(env->mmu_model) && (env->msr_mask & MSR_HVB)) +#endif /* defined(TARGET_PPC64) */ + + ) { + excp = POWERPC_EXCP_PROGRAM; + } + +#ifdef TARGET_PPC64 + /* + * SPEU and VPU share the same IVOR but they exist in different + * processors. SPEU is e500v1/2 only and VPU is e6500 only. + */ + if (excp_model == POWERPC_EXCP_BOOKE && excp == POWERPC_EXCP_VPU) { + excp = POWERPC_EXCP_SPEU; + } +#endif + + vector = env->excp_vectors[excp]; + if (vector == (target_ulong)-1ULL) { + cpu_abort(cs, "Raised an exception without defined vector %d\n", + excp); + } + + vector |= env->excp_prefix; + + switch (excp) { + case POWERPC_EXCP_CRITICAL: /* Critical input */ + switch (excp_model) { + case POWERPC_EXCP_40x: + srr0 = SPR_40x_SRR2; + srr1 = SPR_40x_SRR3; + break; + case POWERPC_EXCP_BOOKE: + srr0 = SPR_BOOKE_CSRR0; + srr1 = SPR_BOOKE_CSRR1; + break; + case POWERPC_EXCP_G2: + break; + default: + goto excp_invalid; + } + break; + case POWERPC_EXCP_MCHECK: /* Machine check exception */ + if (msr_me == 0) { + /* + * 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"); + } + cs->halted = 1; + cpu_interrupt_exittb(cs); + } + if (env->msr_mask & MSR_HVB) { + /* + * ISA specifies HV, but can be delivered to guest with HV + * clear (e.g., see FWNMI in PAPR). + */ + new_msr |= (target_ulong)MSR_HVB; + } + + /* machine check exceptions don't have ME set */ + new_msr &= ~((target_ulong)1 << MSR_ME); + + /* XXX: should also have something loaded in DAR / DSISR */ + switch (excp_model) { + case POWERPC_EXCP_40x: + srr0 = SPR_40x_SRR2; + srr1 = SPR_40x_SRR3; + break; + case POWERPC_EXCP_BOOKE: + /* FIXME: choose one or the other based on CPU type */ + srr0 = SPR_BOOKE_MCSRR0; + srr1 = SPR_BOOKE_MCSRR1; + + env->spr[SPR_BOOKE_CSRR0] = env->nip; + env->spr[SPR_BOOKE_CSRR1] = msr; + break; + default: + break; + } + break; + case POWERPC_EXCP_DSI: /* Data storage exception */ + trace_ppc_excp_dsi(env->spr[SPR_DSISR], env->spr[SPR_DAR]); + break; + case POWERPC_EXCP_ISI: /* Instruction storage exception */ + trace_ppc_excp_isi(msr, env->nip); + msr |= env->error_code; + break; + case POWERPC_EXCP_EXTERNAL: /* External input */ + { + bool lpes0; + + cs = CPU(cpu); + + /* + * Exception targeting modifiers + * + * LPES0 is supported on POWER7/8/9 + * LPES1 is not supported (old iSeries mode) + * + * On anything else, we behave as if LPES0 is 1 + * (externals don't alter MSR:HV) + */ +#if defined(TARGET_PPC64) + if (excp_model == POWERPC_EXCP_POWER7 || + excp_model == POWERPC_EXCP_POWER8 || + excp_model == POWERPC_EXCP_POWER9 || + excp_model == POWERPC_EXCP_POWER10) { + lpes0 = !!(env->spr[SPR_LPCR] & LPCR_LPES0); + } else +#endif /* defined(TARGET_PPC64) */ + { + lpes0 = true; + } + + if (!lpes0) { + new_msr |= (target_ulong)MSR_HVB; + new_msr |= env->msr & ((target_ulong)1 << MSR_RI); + srr0 = SPR_HSRR0; + srr1 = SPR_HSRR1; + } + if (env->mpic_proxy) { + /* IACK the IRQ on delivery */ + env->spr[SPR_BOOKE_EPR] = ldl_phys(cs->as, env->mpic_iack); + } + break; + } + case POWERPC_EXCP_ALIGN: /* Alignment exception */ + /* Get rS/rD and rA from faulting opcode */ + /* + * Note: the opcode fields will not be set properly for a + * direct store load/store, but nobody cares as nobody + * actually uses direct store segments. + */ + env->spr[SPR_DSISR] |= (env->error_code & 0x03FF0000) >> 16; + break; + case POWERPC_EXCP_PROGRAM: /* Program exception */ + switch (env->error_code & ~0xF) { + case POWERPC_EXCP_FP: + if ((msr_fe0 == 0 && msr_fe1 == 0) || msr_fp == 0) { + trace_ppc_excp_fp_ignore(); + cs->exception_index = POWERPC_EXCP_NONE; + env->error_code = 0; + return; + } + + /* + * FP exceptions always have NIP pointing to the faulting + * instruction, so always use store_next and claim we are + * precise in the MSR. + */ + msr |= 0x00100000; + env->spr[SPR_BOOKE_ESR] = ESR_FP; + break; + case POWERPC_EXCP_INVAL: + trace_ppc_excp_inval(env->nip); + msr |= 0x00080000; + env->spr[SPR_BOOKE_ESR] = ESR_PIL; + break; + case POWERPC_EXCP_PRIV: + msr |= 0x00040000; + env->spr[SPR_BOOKE_ESR] = ESR_PPR; + break; + case POWERPC_EXCP_TRAP: + msr |= 0x00020000; + env->spr[SPR_BOOKE_ESR] = ESR_PTR; + break; + default: + /* Should never occur */ + cpu_abort(cs, "Invalid program exception %d. Aborting\n", + env->error_code); + break; + } + break; + case POWERPC_EXCP_SYSCALL: /* System call exception */ + lev = env->error_code; + + if ((lev == 1) && cpu->vhyp) { + dump_hcall(env); + } else { + dump_syscall(env); + } + + /* + * We need to correct the NIP which in this case is supposed + * to point to the next instruction + */ + env->nip += 4; + + /* "PAPR mode" built-in hypercall emulation */ + if ((lev == 1) && cpu->vhyp) { + PPCVirtualHypervisorClass *vhc = + PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp); + vhc->hypercall(cpu->vhyp, cpu); + return; + } + if (lev == 1) { + new_msr |= (target_ulong)MSR_HVB; + } + break; + case POWERPC_EXCP_SYSCALL_VECTORED: /* scv exception */ + lev = env->error_code; + dump_syscall(env); + env->nip += 4; + new_msr |= env->msr & ((target_ulong)1 << MSR_EE); + new_msr |= env->msr & ((target_ulong)1 << MSR_RI); + + vector += lev * 0x20; + + env->lr = env->nip; + env->ctr = msr; + break; + case POWERPC_EXCP_FPU: /* Floating-point unavailable exception */ + case POWERPC_EXCP_APU: /* Auxiliary processor unavailable */ + case POWERPC_EXCP_DECR: /* Decrementer exception */ + break; + case POWERPC_EXCP_FIT: /* Fixed-interval timer interrupt */ + /* FIT on 4xx */ + trace_ppc_excp_print("FIT"); + break; + case POWERPC_EXCP_WDT: /* Watchdog timer interrupt */ + trace_ppc_excp_print("WDT"); + switch (excp_model) { + case POWERPC_EXCP_BOOKE: + srr0 = SPR_BOOKE_CSRR0; + srr1 = SPR_BOOKE_CSRR1; + break; + default: + break; + } + break; + case POWERPC_EXCP_DTLB: /* Data TLB error */ + case POWERPC_EXCP_ITLB: /* Instruction TLB error */ + break; + case POWERPC_EXCP_DEBUG: /* Debug interrupt */ + if (env->flags & POWERPC_FLAG_DE) { + /* FIXME: choose one or the other based on CPU type */ + srr0 = SPR_BOOKE_DSRR0; + srr1 = SPR_BOOKE_DSRR1; + + env->spr[SPR_BOOKE_CSRR0] = env->nip; + env->spr[SPR_BOOKE_CSRR1] = msr; + + /* DBSR already modified by caller */ + } else { + cpu_abort(cs, "Debug exception triggered on unsupported model\n"); + } + break; + case POWERPC_EXCP_SPEU: /* SPE/embedded floating-point unavailable/VPU */ + env->spr[SPR_BOOKE_ESR] = ESR_SPV; + break; + case POWERPC_EXCP_DOORI: /* Embedded doorbell interrupt */ + break; + case POWERPC_EXCP_DOORCI: /* Embedded doorbell critical interrupt */ + srr0 = SPR_BOOKE_CSRR0; + srr1 = SPR_BOOKE_CSRR1; + break; + case POWERPC_EXCP_RESET: /* System reset exception */ + /* A power-saving exception sets ME, otherwise it is unchanged */ + if (msr_pow) { + /* indicate that we resumed from power save mode */ + msr |= 0x10000; + new_msr |= ((target_ulong)1 << MSR_ME); + } + if (env->msr_mask & MSR_HVB) { + /* + * ISA specifies HV, but can be delivered to guest with HV + * clear (e.g., see FWNMI in PAPR, NMI injection in QEMU). + */ + new_msr |= (target_ulong)MSR_HVB; + } else { + if (msr_pow) { + cpu_abort(cs, "Trying to deliver power-saving system reset " + "exception %d with no HV support\n", excp); + } + } + break; + case POWERPC_EXCP_DSEG: /* Data segment exception */ + case POWERPC_EXCP_ISEG: /* Instruction segment exception */ + case POWERPC_EXCP_TRACE: /* Trace exception */ + break; + case POWERPC_EXCP_HISI: /* Hypervisor instruction storage exception */ + msr |= env->error_code; + /* fall through */ + case POWERPC_EXCP_HDECR: /* Hypervisor decrementer exception */ + case POWERPC_EXCP_HDSI: /* Hypervisor data storage exception */ + case POWERPC_EXCP_HDSEG: /* Hypervisor data segment exception */ + case POWERPC_EXCP_HISEG: /* Hypervisor instruction segment exception */ + case POWERPC_EXCP_SDOOR_HV: /* Hypervisor Doorbell interrupt */ + case POWERPC_EXCP_HV_EMU: + case POWERPC_EXCP_HVIRT: /* Hypervisor virtualization */ + srr0 = SPR_HSRR0; + srr1 = SPR_HSRR1; + new_msr |= (target_ulong)MSR_HVB; + new_msr |= env->msr & ((target_ulong)1 << MSR_RI); + break; + case POWERPC_EXCP_VPU: /* Vector unavailable exception */ + case POWERPC_EXCP_VSXU: /* VSX unavailable exception */ + case POWERPC_EXCP_FU: /* Facility unavailable exception */ +#ifdef TARGET_PPC64 + env->spr[SPR_FSCR] |= ((target_ulong)env->error_code << 56); +#endif + break; + case POWERPC_EXCP_HV_FU: /* Hypervisor Facility Unavailable Exception */ +#ifdef TARGET_PPC64 + env->spr[SPR_HFSCR] |= ((target_ulong)env->error_code << FSCR_IC_POS); + srr0 = SPR_HSRR0; + srr1 = SPR_HSRR1; + new_msr |= (target_ulong)MSR_HVB; + new_msr |= env->msr & ((target_ulong)1 << MSR_RI); +#endif + break; + case POWERPC_EXCP_PIT: /* Programmable interval timer interrupt */ + trace_ppc_excp_print("PIT"); + break; + case POWERPC_EXCP_IFTLB: /* Instruction fetch TLB error */ + case POWERPC_EXCP_DLTLB: /* Data load TLB miss */ + case POWERPC_EXCP_DSTLB: /* Data store TLB miss */ + switch (excp_model) { + case POWERPC_EXCP_602: + case POWERPC_EXCP_603: + case POWERPC_EXCP_G2: + /* Swap temporary saved registers with GPRs */ + if (!(new_msr & ((target_ulong)1 << MSR_TGPR))) { + new_msr |= (target_ulong)1 << MSR_TGPR; + hreg_swap_gpr_tgpr(env); + } + /* fall through */ + case POWERPC_EXCP_7x5: + ppc_excp_debug_sw_tlb(env, excp); + + msr |= env->crf[0] << 28; + msr |= env->error_code; /* key, D/I, S/L bits */ + /* Set way using a LRU mechanism */ + msr |= ((env->last_way + 1) & (env->nb_ways - 1)) << 17; + break; + default: + cpu_abort(cs, "Invalid TLB miss exception\n"); + break; + } + break; + case POWERPC_EXCP_EFPDI: /* Embedded floating-point data interrupt */ + case POWERPC_EXCP_EFPRI: /* Embedded floating-point round interrupt */ + case POWERPC_EXCP_EPERFM: /* Embedded performance monitor interrupt */ + case POWERPC_EXCP_IO: /* IO error exception */ + case POWERPC_EXCP_RUNM: /* Run mode exception */ + case POWERPC_EXCP_EMUL: /* Emulation trap exception */ + case POWERPC_EXCP_FPA: /* Floating-point assist exception */ + case POWERPC_EXCP_DABR: /* Data address breakpoint */ + case POWERPC_EXCP_IABR: /* Instruction address breakpoint */ + case POWERPC_EXCP_SMI: /* System management interrupt */ + case POWERPC_EXCP_THERM: /* Thermal interrupt */ + case POWERPC_EXCP_PERFM: /* Embedded performance monitor interrupt */ + case POWERPC_EXCP_VPUA: /* Vector assist exception */ + case POWERPC_EXCP_SOFTP: /* Soft patch exception */ + case POWERPC_EXCP_MAINT: /* Maintenance exception */ + case POWERPC_EXCP_MEXTBR: /* Maskable external breakpoint */ + case POWERPC_EXCP_NMEXTBR: /* Non maskable external breakpoint */ + cpu_abort(cs, "%s exception not implemented\n", + powerpc_excp_name(excp)); + break; + default: + excp_invalid: + cpu_abort(cs, "Invalid PowerPC exception %d. Aborting\n", excp); + break; + } + + /* Sanity check */ + if (!(env->msr_mask & MSR_HVB)) { + if (new_msr & MSR_HVB) { + cpu_abort(cs, "Trying to deliver HV exception (MSR) %d with " + "no HV support\n", excp); + } + if (srr0 == SPR_HSRR0) { + cpu_abort(cs, "Trying to deliver HV exception (HSRR) %d with " + "no HV support\n", excp); + } + } + + /* + * Sort out endianness of interrupt, this differs depending on the + * CPU, the HV mode, etc... + */ + if (ppc_interrupts_little_endian(cpu, !!(new_msr & MSR_HVB))) { + new_msr |= (target_ulong)1 << MSR_LE; + } + +#if defined(TARGET_PPC64) + if (excp_model == POWERPC_EXCP_BOOKE) { + if (env->spr[SPR_BOOKE_EPCR] & EPCR_ICM) { + /* Cat.64-bit: EPCR.ICM is copied to MSR.CM */ + new_msr |= (target_ulong)1 << MSR_CM; + } else { + vector = (uint32_t)vector; + } + } else { + if (!msr_isf && !mmu_is_64bit(env->mmu_model)) { + vector = (uint32_t)vector; + } else { + new_msr |= (target_ulong)1 << MSR_SF; + } + } +#endif + + if (excp != POWERPC_EXCP_SYSCALL_VECTORED) { + /* Save PC */ + env->spr[srr0] = env->nip; + + /* Save MSR */ + env->spr[srr1] = msr; + } + + /* This can update new_msr and vector if AIL applies */ + ppc_excp_apply_ail(cpu, excp_model, excp, msr, &new_msr, &vector); + + powerpc_set_excp_state(cpu, vector, new_msr); +} + /* * Note that this function should be greatly optimized when called * with a constant excp, from ppc_hw_interrupt @@ -872,6 +1343,9 @@ static void powerpc_excp(PowerPCCPU *cpu, int excp) CPUPPCState *env = &cpu->env; switch (env->excp_model) { + case POWERPC_EXCP_40x: + powerpc_excp_40x(cpu, excp); + break; default: powerpc_excp_legacy(cpu, excp); } From 495fc7ff9677edc31b97084fb17db91737ce4ea4 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Fri, 28 Jan 2022 13:15:04 +0100 Subject: [PATCH 048/460] target/ppc: Simplify powerpc_excp_40x MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Differences from the generic powerpc_excp code: - Not BookE, so some MSR bits are cleared at interrupt dispatch; - No MSR_HV or MSR_LE; - No power saving states; - No Hypervisor Emulation Assistance; - Not 64 bits; - No System call vectored; - No Interrupts Little Endian; - No Alternate Interrupt Location. Exceptions used: POWERPC_EXCP_ALIGN POWERPC_EXCP_CRITICAL POWERPC_EXCP_DEBUG POWERPC_EXCP_DSI POWERPC_EXCP_DTLB POWERPC_EXCP_EXTERNAL POWERPC_EXCP_FIT POWERPC_EXCP_ISI POWERPC_EXCP_ITLB POWERPC_EXCP_MCHECK POWERPC_EXCP_PIT POWERPC_EXCP_PROGRAM POWERPC_EXCP_SYSCALL POWERPC_EXCP_WDT Signed-off-by: Fabiano Rosas Reviewed-by: David Gibson Message-Id: <20220118184448.852996-5-farosas@linux.ibm.com> Signed-off-by: Cédric Le Goater --- target/ppc/excp_helper.c | 205 ++------------------------------------- 1 file changed, 10 insertions(+), 195 deletions(-) diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index 5dc948aec2..341a765bd4 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -409,54 +409,26 @@ static void powerpc_excp_40x(PowerPCCPU *cpu, int excp) excp, env->error_code); /* new srr1 value excluding must-be-zero bits */ - if (excp_model == POWERPC_EXCP_BOOKE) { - msr = env->msr; - } else { - msr = env->msr & ~0x783f0000ULL; - } + msr = env->msr & ~0x783f0000ULL; /* - * new interrupt handler msr preserves existing HV and ME unless - * explicitly overriden + * new interrupt handler msr preserves existing ME unless + * explicitly overriden. */ - new_msr = env->msr & (((target_ulong)1 << MSR_ME) | MSR_HVB); + new_msr = env->msr & (((target_ulong)1 << MSR_ME)); /* target registers */ srr0 = SPR_SRR0; srr1 = SPR_SRR1; - /* - * check for special resume at 0x100 from doze/nap/sleep/winkle on - * P7/P8/P9 - */ - if (env->resume_as_sreset) { - excp = powerpc_reset_wakeup(cs, env, excp, &msr); - } - /* * Hypervisor emulation assistance interrupt only exists on server - * arch 2.05 server or later. We also don't want to generate it if - * we don't have HVB in msr_mask (PAPR mode). + * arch 2.05 server or later. */ - if (excp == POWERPC_EXCP_HV_EMU -#if defined(TARGET_PPC64) - && !(mmu_is_64bit(env->mmu_model) && (env->msr_mask & MSR_HVB)) -#endif /* defined(TARGET_PPC64) */ - - ) { + if (excp == POWERPC_EXCP_HV_EMU) { excp = POWERPC_EXCP_PROGRAM; } -#ifdef TARGET_PPC64 - /* - * SPEU and VPU share the same IVOR but they exist in different - * processors. SPEU is e500v1/2 only and VPU is e6500 only. - */ - if (excp_model == POWERPC_EXCP_BOOKE && excp == POWERPC_EXCP_VPU) { - excp = POWERPC_EXCP_SPEU; - } -#endif - vector = env->excp_vectors[excp]; if (vector == (target_ulong)-1ULL) { cpu_abort(cs, "Raised an exception without defined vector %d\n", @@ -645,24 +617,7 @@ static void powerpc_excp_40x(PowerPCCPU *cpu, int excp) new_msr |= (target_ulong)MSR_HVB; } break; - case POWERPC_EXCP_SYSCALL_VECTORED: /* scv exception */ - lev = env->error_code; - dump_syscall(env); - env->nip += 4; - new_msr |= env->msr & ((target_ulong)1 << MSR_EE); - new_msr |= env->msr & ((target_ulong)1 << MSR_RI); - - vector += lev * 0x20; - - env->lr = env->nip; - env->ctr = msr; - break; - case POWERPC_EXCP_FPU: /* Floating-point unavailable exception */ - case POWERPC_EXCP_APU: /* Auxiliary processor unavailable */ - case POWERPC_EXCP_DECR: /* Decrementer exception */ - break; case POWERPC_EXCP_FIT: /* Fixed-interval timer interrupt */ - /* FIT on 4xx */ trace_ppc_excp_print("FIT"); break; case POWERPC_EXCP_WDT: /* Watchdog timer interrupt */ @@ -693,119 +648,9 @@ static void powerpc_excp_40x(PowerPCCPU *cpu, int excp) cpu_abort(cs, "Debug exception triggered on unsupported model\n"); } break; - case POWERPC_EXCP_SPEU: /* SPE/embedded floating-point unavailable/VPU */ - env->spr[SPR_BOOKE_ESR] = ESR_SPV; - break; - case POWERPC_EXCP_DOORI: /* Embedded doorbell interrupt */ - break; - case POWERPC_EXCP_DOORCI: /* Embedded doorbell critical interrupt */ - srr0 = SPR_BOOKE_CSRR0; - srr1 = SPR_BOOKE_CSRR1; - break; - case POWERPC_EXCP_RESET: /* System reset exception */ - /* A power-saving exception sets ME, otherwise it is unchanged */ - if (msr_pow) { - /* indicate that we resumed from power save mode */ - msr |= 0x10000; - new_msr |= ((target_ulong)1 << MSR_ME); - } - if (env->msr_mask & MSR_HVB) { - /* - * ISA specifies HV, but can be delivered to guest with HV - * clear (e.g., see FWNMI in PAPR, NMI injection in QEMU). - */ - new_msr |= (target_ulong)MSR_HVB; - } else { - if (msr_pow) { - cpu_abort(cs, "Trying to deliver power-saving system reset " - "exception %d with no HV support\n", excp); - } - } - break; - case POWERPC_EXCP_DSEG: /* Data segment exception */ - case POWERPC_EXCP_ISEG: /* Instruction segment exception */ - case POWERPC_EXCP_TRACE: /* Trace exception */ - break; - case POWERPC_EXCP_HISI: /* Hypervisor instruction storage exception */ - msr |= env->error_code; - /* fall through */ - case POWERPC_EXCP_HDECR: /* Hypervisor decrementer exception */ - case POWERPC_EXCP_HDSI: /* Hypervisor data storage exception */ - case POWERPC_EXCP_HDSEG: /* Hypervisor data segment exception */ - case POWERPC_EXCP_HISEG: /* Hypervisor instruction segment exception */ - case POWERPC_EXCP_SDOOR_HV: /* Hypervisor Doorbell interrupt */ - case POWERPC_EXCP_HV_EMU: - case POWERPC_EXCP_HVIRT: /* Hypervisor virtualization */ - srr0 = SPR_HSRR0; - srr1 = SPR_HSRR1; - new_msr |= (target_ulong)MSR_HVB; - new_msr |= env->msr & ((target_ulong)1 << MSR_RI); - break; - case POWERPC_EXCP_VPU: /* Vector unavailable exception */ - case POWERPC_EXCP_VSXU: /* VSX unavailable exception */ - case POWERPC_EXCP_FU: /* Facility unavailable exception */ -#ifdef TARGET_PPC64 - env->spr[SPR_FSCR] |= ((target_ulong)env->error_code << 56); -#endif - break; - case POWERPC_EXCP_HV_FU: /* Hypervisor Facility Unavailable Exception */ -#ifdef TARGET_PPC64 - env->spr[SPR_HFSCR] |= ((target_ulong)env->error_code << FSCR_IC_POS); - srr0 = SPR_HSRR0; - srr1 = SPR_HSRR1; - new_msr |= (target_ulong)MSR_HVB; - new_msr |= env->msr & ((target_ulong)1 << MSR_RI); -#endif - break; case POWERPC_EXCP_PIT: /* Programmable interval timer interrupt */ trace_ppc_excp_print("PIT"); break; - case POWERPC_EXCP_IFTLB: /* Instruction fetch TLB error */ - case POWERPC_EXCP_DLTLB: /* Data load TLB miss */ - case POWERPC_EXCP_DSTLB: /* Data store TLB miss */ - switch (excp_model) { - case POWERPC_EXCP_602: - case POWERPC_EXCP_603: - case POWERPC_EXCP_G2: - /* Swap temporary saved registers with GPRs */ - if (!(new_msr & ((target_ulong)1 << MSR_TGPR))) { - new_msr |= (target_ulong)1 << MSR_TGPR; - hreg_swap_gpr_tgpr(env); - } - /* fall through */ - case POWERPC_EXCP_7x5: - ppc_excp_debug_sw_tlb(env, excp); - - msr |= env->crf[0] << 28; - msr |= env->error_code; /* key, D/I, S/L bits */ - /* Set way using a LRU mechanism */ - msr |= ((env->last_way + 1) & (env->nb_ways - 1)) << 17; - break; - default: - cpu_abort(cs, "Invalid TLB miss exception\n"); - break; - } - break; - case POWERPC_EXCP_EFPDI: /* Embedded floating-point data interrupt */ - case POWERPC_EXCP_EFPRI: /* Embedded floating-point round interrupt */ - case POWERPC_EXCP_EPERFM: /* Embedded performance monitor interrupt */ - case POWERPC_EXCP_IO: /* IO error exception */ - case POWERPC_EXCP_RUNM: /* Run mode exception */ - case POWERPC_EXCP_EMUL: /* Emulation trap exception */ - case POWERPC_EXCP_FPA: /* Floating-point assist exception */ - case POWERPC_EXCP_DABR: /* Data address breakpoint */ - case POWERPC_EXCP_IABR: /* Instruction address breakpoint */ - case POWERPC_EXCP_SMI: /* System management interrupt */ - case POWERPC_EXCP_THERM: /* Thermal interrupt */ - case POWERPC_EXCP_PERFM: /* Embedded performance monitor interrupt */ - case POWERPC_EXCP_VPUA: /* Vector assist exception */ - case POWERPC_EXCP_SOFTP: /* Soft patch exception */ - case POWERPC_EXCP_MAINT: /* Maintenance exception */ - case POWERPC_EXCP_MEXTBR: /* Maskable external breakpoint */ - case POWERPC_EXCP_NMEXTBR: /* Non maskable external breakpoint */ - cpu_abort(cs, "%s exception not implemented\n", - powerpc_excp_name(excp)); - break; default: excp_invalid: cpu_abort(cs, "Invalid PowerPC exception %d. Aborting\n", excp); @@ -824,41 +669,11 @@ static void powerpc_excp_40x(PowerPCCPU *cpu, int excp) } } - /* - * Sort out endianness of interrupt, this differs depending on the - * CPU, the HV mode, etc... - */ - if (ppc_interrupts_little_endian(cpu, !!(new_msr & MSR_HVB))) { - new_msr |= (target_ulong)1 << MSR_LE; - } + /* Save PC */ + env->spr[srr0] = env->nip; -#if defined(TARGET_PPC64) - if (excp_model == POWERPC_EXCP_BOOKE) { - if (env->spr[SPR_BOOKE_EPCR] & EPCR_ICM) { - /* Cat.64-bit: EPCR.ICM is copied to MSR.CM */ - new_msr |= (target_ulong)1 << MSR_CM; - } else { - vector = (uint32_t)vector; - } - } else { - if (!msr_isf && !mmu_is_64bit(env->mmu_model)) { - vector = (uint32_t)vector; - } else { - new_msr |= (target_ulong)1 << MSR_SF; - } - } -#endif - - if (excp != POWERPC_EXCP_SYSCALL_VECTORED) { - /* Save PC */ - env->spr[srr0] = env->nip; - - /* Save MSR */ - env->spr[srr1] = msr; - } - - /* This can update new_msr and vector if AIL applies */ - ppc_excp_apply_ail(cpu, excp_model, excp, msr, &new_msr, &vector); + /* Save MSR */ + env->spr[srr1] = msr; powerpc_set_excp_state(cpu, vector, new_msr); } From ba96828ec25f04fd6b0b93076be506fe5adc6f6d Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Fri, 28 Jan 2022 13:15:04 +0100 Subject: [PATCH 049/460] target/ppc: 405: Critical exceptions cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In powerpc_excp_40x the Critical exception is now for 405 only, so we can remove the BookE and G2 blocks. Signed-off-by: Fabiano Rosas Reviewed-by: David Gibson Reviewed-by: Richard Henderson Message-Id: <20220118184448.852996-6-farosas@linux.ibm.com> Signed-off-by: Cédric Le Goater --- target/ppc/excp_helper.c | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index 341a765bd4..aafc381eb0 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -439,20 +439,8 @@ static void powerpc_excp_40x(PowerPCCPU *cpu, int excp) switch (excp) { case POWERPC_EXCP_CRITICAL: /* Critical input */ - switch (excp_model) { - case POWERPC_EXCP_40x: - srr0 = SPR_40x_SRR2; - srr1 = SPR_40x_SRR3; - break; - case POWERPC_EXCP_BOOKE: - srr0 = SPR_BOOKE_CSRR0; - srr1 = SPR_BOOKE_CSRR1; - break; - case POWERPC_EXCP_G2: - break; - default: - goto excp_invalid; - } + srr0 = SPR_40x_SRR2; + srr1 = SPR_40x_SRR3; break; case POWERPC_EXCP_MCHECK: /* Machine check exception */ if (msr_me == 0) { @@ -652,7 +640,6 @@ static void powerpc_excp_40x(PowerPCCPU *cpu, int excp) trace_ppc_excp_print("PIT"); break; default: - excp_invalid: cpu_abort(cs, "Invalid PowerPC exception %d. Aborting\n", excp); break; } From 2149e6518032e2209c7520bda6aa38b98850def6 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Fri, 28 Jan 2022 13:15:04 +0100 Subject: [PATCH 050/460] target/ppc: 405: Machine check exception cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit powerpc_excp_40x applies only to the 405, so remove HV code and references to BookE. Signed-off-by: Fabiano Rosas Reviewed-by: David Gibson Message-Id: <20220118184448.852996-7-farosas@linux.ibm.com> Signed-off-by: Cédric Le Goater --- target/ppc/excp_helper.c | 26 ++------------------------ 1 file changed, 2 insertions(+), 24 deletions(-) diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index aafc381eb0..3894d36685 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -457,34 +457,12 @@ static void powerpc_excp_40x(PowerPCCPU *cpu, int excp) cs->halted = 1; cpu_interrupt_exittb(cs); } - if (env->msr_mask & MSR_HVB) { - /* - * ISA specifies HV, but can be delivered to guest with HV - * clear (e.g., see FWNMI in PAPR). - */ - new_msr |= (target_ulong)MSR_HVB; - } /* machine check exceptions don't have ME set */ new_msr &= ~((target_ulong)1 << MSR_ME); - /* XXX: should also have something loaded in DAR / DSISR */ - switch (excp_model) { - case POWERPC_EXCP_40x: - srr0 = SPR_40x_SRR2; - srr1 = SPR_40x_SRR3; - break; - case POWERPC_EXCP_BOOKE: - /* FIXME: choose one or the other based on CPU type */ - srr0 = SPR_BOOKE_MCSRR0; - srr1 = SPR_BOOKE_MCSRR1; - - env->spr[SPR_BOOKE_CSRR0] = env->nip; - env->spr[SPR_BOOKE_CSRR1] = msr; - break; - default: - break; - } + srr0 = SPR_40x_SRR2; + srr1 = SPR_40x_SRR3; break; case POWERPC_EXCP_DSI: /* Data storage exception */ trace_ppc_excp_dsi(env->spr[SPR_DSISR], env->spr[SPR_DAR]); From 9026e99c894a85774b8fdf4bd32a2233863fc2f8 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Fri, 28 Jan 2022 13:15:04 +0100 Subject: [PATCH 051/460] target/ppc: 405: External exception cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 405 has no MSR_HV and EPR is BookE only so we can remove it all. Signed-off-by: Fabiano Rosas Reviewed-by: David Gibson Message-Id: <20220118184448.852996-8-farosas@linux.ibm.com> Signed-off-by: Cédric Le Goater --- target/ppc/excp_helper.c | 37 ------------------------------------- 1 file changed, 37 deletions(-) diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index 3894d36685..069288a5c8 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -472,44 +472,7 @@ static void powerpc_excp_40x(PowerPCCPU *cpu, int excp) msr |= env->error_code; break; case POWERPC_EXCP_EXTERNAL: /* External input */ - { - bool lpes0; - - cs = CPU(cpu); - - /* - * Exception targeting modifiers - * - * LPES0 is supported on POWER7/8/9 - * LPES1 is not supported (old iSeries mode) - * - * On anything else, we behave as if LPES0 is 1 - * (externals don't alter MSR:HV) - */ -#if defined(TARGET_PPC64) - if (excp_model == POWERPC_EXCP_POWER7 || - excp_model == POWERPC_EXCP_POWER8 || - excp_model == POWERPC_EXCP_POWER9 || - excp_model == POWERPC_EXCP_POWER10) { - lpes0 = !!(env->spr[SPR_LPCR] & LPCR_LPES0); - } else -#endif /* defined(TARGET_PPC64) */ - { - lpes0 = true; - } - - if (!lpes0) { - new_msr |= (target_ulong)MSR_HVB; - new_msr |= env->msr & ((target_ulong)1 << MSR_RI); - srr0 = SPR_HSRR0; - srr1 = SPR_HSRR1; - } - if (env->mpic_proxy) { - /* IACK the IRQ on delivery */ - env->spr[SPR_BOOKE_EPR] = ldl_phys(cs->as, env->mpic_iack); - } break; - } case POWERPC_EXCP_ALIGN: /* Alignment exception */ /* Get rS/rD and rA from faulting opcode */ /* From 8428cdb245099dcf03c32e1b66f530ada65a6e83 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Fri, 28 Jan 2022 13:15:04 +0100 Subject: [PATCH 052/460] target/ppc: 405: System call exception cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There's no sc 1. Signed-off-by: Fabiano Rosas Reviewed-by: Cédric Le Goater Reviewed-by: Richard Henderson Message-Id: <20220118184448.852996-9-farosas@linux.ibm.com> Signed-off-by: Cédric Le Goater --- target/ppc/excp_helper.c | 21 ++------------------- 1 file changed, 2 insertions(+), 19 deletions(-) diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index 069288a5c8..1f915f607d 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -398,7 +398,7 @@ static void powerpc_excp_40x(PowerPCCPU *cpu, int excp) CPUPPCState *env = &cpu->env; int excp_model = env->excp_model; target_ulong msr, new_msr, vector; - int srr0, srr1, lev = -1; + int srr0, srr1; if (excp <= POWERPC_EXCP_NONE || excp >= POWERPC_EXCP_NB) { cpu_abort(cs, "Invalid PowerPC exception %d. Aborting\n", excp); @@ -521,30 +521,13 @@ static void powerpc_excp_40x(PowerPCCPU *cpu, int excp) } break; case POWERPC_EXCP_SYSCALL: /* System call exception */ - lev = env->error_code; - - if ((lev == 1) && cpu->vhyp) { - dump_hcall(env); - } else { - dump_syscall(env); - } + dump_syscall(env); /* * We need to correct the NIP which in this case is supposed * to point to the next instruction */ env->nip += 4; - - /* "PAPR mode" built-in hypercall emulation */ - if ((lev == 1) && cpu->vhyp) { - PPCVirtualHypervisorClass *vhc = - PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp); - vhc->hypercall(cpu->vhyp, cpu); - return; - } - if (lev == 1) { - new_msr |= (target_ulong)MSR_HVB; - } break; case POWERPC_EXCP_FIT: /* Fixed-interval timer interrupt */ trace_ppc_excp_print("FIT"); From 66b5ad561552c5c77058502ea3c7f04316937b64 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Fri, 28 Jan 2022 13:15:04 +0100 Subject: [PATCH 053/460] target/ppc: 405: Alignment exception cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is no DSISR in the 405. It uses DEAR which we already set earlier at ppc_cpu_do_unaligned_access. Signed-off-by: Fabiano Rosas Reviewed-by: David Gibson Signed-off-by: Cédric Le Goater --- target/ppc/excp_helper.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index 1f915f607d..55f6b0e981 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -474,13 +474,6 @@ static void powerpc_excp_40x(PowerPCCPU *cpu, int excp) case POWERPC_EXCP_EXTERNAL: /* External input */ break; case POWERPC_EXCP_ALIGN: /* Alignment exception */ - /* Get rS/rD and rA from faulting opcode */ - /* - * Note: the opcode fields will not be set properly for a - * direct store load/store, but nobody cares as nobody - * actually uses direct store segments. - */ - env->spr[SPR_DSISR] |= (env->error_code & 0x03FF0000) >> 16; break; case POWERPC_EXCP_PROGRAM: /* Program exception */ switch (env->error_code & ~0xF) { From 4d8ac1d15ec34d0967c7e51e375e72c522c1e6b5 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Fri, 28 Jan 2022 13:15:04 +0100 Subject: [PATCH 054/460] target/ppc: 405: Debug exception cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The current Debug exception dispatch is the BookE one, so it is different from the 405. We effectively don't support the 405 Debug exception. This patch removes the BookE code and moves the DEBUG into the "not implemented" block. Note that there is in theory a functional change here since we now abort when a Debug exception happens. However, given how it was never implemented, I don't believe this to have ever been dispatched for the 405. Signed-off-by: Fabiano Rosas Reviewed-by: David Gibson Message-Id: <20220118184448.852996-11-farosas@linux.ibm.com> Signed-off-by: Cédric Le Goater --- target/ppc/excp_helper.c | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index 55f6b0e981..89fc40bb73 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -539,23 +539,13 @@ static void powerpc_excp_40x(PowerPCCPU *cpu, int excp) case POWERPC_EXCP_DTLB: /* Data TLB error */ case POWERPC_EXCP_ITLB: /* Instruction TLB error */ break; - case POWERPC_EXCP_DEBUG: /* Debug interrupt */ - if (env->flags & POWERPC_FLAG_DE) { - /* FIXME: choose one or the other based on CPU type */ - srr0 = SPR_BOOKE_DSRR0; - srr1 = SPR_BOOKE_DSRR1; - - env->spr[SPR_BOOKE_CSRR0] = env->nip; - env->spr[SPR_BOOKE_CSRR1] = msr; - - /* DBSR already modified by caller */ - } else { - cpu_abort(cs, "Debug exception triggered on unsupported model\n"); - } - break; case POWERPC_EXCP_PIT: /* Programmable interval timer interrupt */ trace_ppc_excp_print("PIT"); break; + case POWERPC_EXCP_DEBUG: /* Debug interrupt */ + cpu_abort(cs, "%s exception not implemented\n", + powerpc_excp_name(excp)); + break; default: cpu_abort(cs, "Invalid PowerPC exception %d. Aborting\n", excp); break; From f9911e1e5513ebf661ae871ae31269a9a1cfabdc Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Fri, 28 Jan 2022 13:15:05 +0100 Subject: [PATCH 055/460] target/ppc: 405: Data Storage exception cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 405 has no DSISR or DAR, so convert the trace entry to use ESR and DEAR instead. Signed-off-by: Fabiano Rosas Reviewed-by: Cédric Le Goater [ clg : - changed registers to ESR and DEAR. - updated commit log ] Message-Id: <20220118184448.852996-12-farosas@linux.ibm.com> Signed-off-by: Cédric Le Goater --- 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 89fc40bb73..deba12f4f3 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -465,7 +465,7 @@ static void powerpc_excp_40x(PowerPCCPU *cpu, int excp) srr1 = SPR_40x_SRR3; break; case POWERPC_EXCP_DSI: /* Data storage exception */ - trace_ppc_excp_dsi(env->spr[SPR_DSISR], env->spr[SPR_DAR]); + trace_ppc_excp_dsi(env->spr[SPR_40x_ESR], env->spr[SPR_40x_DEAR]); break; case POWERPC_EXCP_ISI: /* Instruction storage exception */ trace_ppc_excp_isi(msr, env->nip); From 35f579f5c21682311039f84e2e81254937e6ff78 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Fri, 28 Jan 2022 13:15:05 +0100 Subject: [PATCH 056/460] target/ppc: 405: Instruction storage interrupt cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 405 ISI does not set SRR1 with any exception syndrome bits, only a clean copy of the MSR. Signed-off-by: Fabiano Rosas Reviewed-by: Cédric Le Goater [ clg : Fixed removal which was done in the wrong routine ] Message-Id: <20220118184448.852996-13-farosas@linux.ibm.com> Signed-off-by: Cédric Le Goater --- target/ppc/excp_helper.c | 1 - 1 file changed, 1 deletion(-) diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index deba12f4f3..7d89bd0651 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -469,7 +469,6 @@ static void powerpc_excp_40x(PowerPCCPU *cpu, int excp) break; case POWERPC_EXCP_ISI: /* Instruction storage exception */ trace_ppc_excp_isi(msr, env->nip); - msr |= env->error_code; break; case POWERPC_EXCP_EXTERNAL: /* External input */ break; From 64e62cfbec19ed22d097645219bdddb55df6f562 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Fri, 28 Jan 2022 13:15:05 +0100 Subject: [PATCH 057/460] target/ppc: 405: Program exception cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 405 Program Interrupt does not set SRR1 with any diagnostic bits, just a clean copy of the MSR. We're using the BookE Exception Syndrome Register which is different from the 405. Signed-off-by: Fabiano Rosas Reviewed-by: Cédric Le Goater [ clg: restored SPR_40x_ESR settings ] Message-Id: <20220118184448.852996-14-farosas@linux.ibm.com> Signed-off-by: Cédric Le Goater --- target/ppc/excp_helper.c | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index 7d89bd0651..b528457d92 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -483,30 +483,19 @@ static void powerpc_excp_40x(PowerPCCPU *cpu, int excp) env->error_code = 0; return; } - - /* - * FP exceptions always have NIP pointing to the faulting - * instruction, so always use store_next and claim we are - * precise in the MSR. - */ - msr |= 0x00100000; - env->spr[SPR_BOOKE_ESR] = ESR_FP; + env->spr[SPR_40x_ESR] = ESR_FP; break; case POWERPC_EXCP_INVAL: trace_ppc_excp_inval(env->nip); - msr |= 0x00080000; - env->spr[SPR_BOOKE_ESR] = ESR_PIL; + env->spr[SPR_40x_ESR] = ESR_PIL; break; case POWERPC_EXCP_PRIV: - msr |= 0x00040000; - env->spr[SPR_BOOKE_ESR] = ESR_PPR; + env->spr[SPR_40x_ESR] = ESR_PPR; break; case POWERPC_EXCP_TRAP: - msr |= 0x00020000; - env->spr[SPR_BOOKE_ESR] = ESR_PTR; + env->spr[SPR_40x_ESR] = ESR_PTR; break; default: - /* Should never occur */ cpu_abort(cs, "Invalid program exception %d. Aborting\n", env->error_code); break; From 1afe57802ab0127b31b323be171ba9c943a04133 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Fri, 28 Jan 2022 13:15:05 +0100 Subject: [PATCH 058/460] target/ppc: 405: Watchdog timer exception cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove references to BookE. Signed-off-by: Fabiano Rosas Reviewed-by: Cédric Le Goater Message-Id: <20220118184448.852996-15-farosas@linux.ibm.com> Signed-off-by: Cédric Le Goater --- target/ppc/excp_helper.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index b528457d92..c3ff798d9d 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -396,7 +396,6 @@ static void powerpc_excp_40x(PowerPCCPU *cpu, int excp) { CPUState *cs = CPU(cpu); CPUPPCState *env = &cpu->env; - int excp_model = env->excp_model; target_ulong msr, new_msr, vector; int srr0, srr1; @@ -515,14 +514,6 @@ static void powerpc_excp_40x(PowerPCCPU *cpu, int excp) break; case POWERPC_EXCP_WDT: /* Watchdog timer interrupt */ trace_ppc_excp_print("WDT"); - switch (excp_model) { - case POWERPC_EXCP_BOOKE: - srr0 = SPR_BOOKE_CSRR0; - srr1 = SPR_BOOKE_CSRR1; - break; - default: - break; - } break; case POWERPC_EXCP_DTLB: /* Data TLB error */ case POWERPC_EXCP_ITLB: /* Instruction TLB error */ From 9f338e4da17a73cd1b78bbef02f797d7edfd133a Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Fri, 28 Jan 2022 13:15:05 +0100 Subject: [PATCH 059/460] target/ppc: Introduce powerpc_excp_books MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce a new powerpc_excp function specific for BookS CPUs. This commit copies powerpc_excp_legacy verbatim so the next one has a clean diff. Signed-off-by: Fabiano Rosas Reviewed-by: Cédric Le Goater Message-Id: <20220124184605.999353-2-farosas@linux.ibm.com> Signed-off-by: Cédric Le Goater --- target/ppc/excp_helper.c | 478 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 478 insertions(+) diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index c3ff798d9d..a2a605f8f5 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -551,6 +551,477 @@ static void powerpc_excp_40x(PowerPCCPU *cpu, int excp) powerpc_set_excp_state(cpu, vector, new_msr); } +static void powerpc_excp_books(PowerPCCPU *cpu, int excp) +{ + CPUState *cs = CPU(cpu); + CPUPPCState *env = &cpu->env; + int excp_model = env->excp_model; + target_ulong msr, new_msr, vector; + int srr0, srr1, lev = -1; + + if (excp <= POWERPC_EXCP_NONE || excp >= POWERPC_EXCP_NB) { + cpu_abort(cs, "Invalid PowerPC exception %d. Aborting\n", excp); + } + + qemu_log_mask(CPU_LOG_INT, "Raise exception at " TARGET_FMT_lx + " => %s (%d) error=%02x\n", env->nip, powerpc_excp_name(excp), + excp, env->error_code); + + /* new srr1 value excluding must-be-zero bits */ + if (excp_model == POWERPC_EXCP_BOOKE) { + msr = env->msr; + } else { + msr = env->msr & ~0x783f0000ULL; + } + + /* + * new interrupt handler msr preserves existing HV and ME unless + * explicitly overriden + */ + new_msr = env->msr & (((target_ulong)1 << MSR_ME) | MSR_HVB); + + /* target registers */ + srr0 = SPR_SRR0; + srr1 = SPR_SRR1; + + /* + * check for special resume at 0x100 from doze/nap/sleep/winkle on + * P7/P8/P9 + */ + if (env->resume_as_sreset) { + excp = powerpc_reset_wakeup(cs, env, excp, &msr); + } + + /* + * Hypervisor emulation assistance interrupt only exists on server + * arch 2.05 server or later. We also don't want to generate it if + * we don't have HVB in msr_mask (PAPR mode). + */ + if (excp == POWERPC_EXCP_HV_EMU +#if defined(TARGET_PPC64) + && !(mmu_is_64bit(env->mmu_model) && (env->msr_mask & MSR_HVB)) +#endif /* defined(TARGET_PPC64) */ + + ) { + excp = POWERPC_EXCP_PROGRAM; + } + +#ifdef TARGET_PPC64 + /* + * SPEU and VPU share the same IVOR but they exist in different + * processors. SPEU is e500v1/2 only and VPU is e6500 only. + */ + if (excp_model == POWERPC_EXCP_BOOKE && excp == POWERPC_EXCP_VPU) { + excp = POWERPC_EXCP_SPEU; + } +#endif + + vector = env->excp_vectors[excp]; + if (vector == (target_ulong)-1ULL) { + cpu_abort(cs, "Raised an exception without defined vector %d\n", + excp); + } + + vector |= env->excp_prefix; + + switch (excp) { + case POWERPC_EXCP_CRITICAL: /* Critical input */ + switch (excp_model) { + case POWERPC_EXCP_40x: + srr0 = SPR_40x_SRR2; + srr1 = SPR_40x_SRR3; + break; + case POWERPC_EXCP_BOOKE: + srr0 = SPR_BOOKE_CSRR0; + srr1 = SPR_BOOKE_CSRR1; + break; + case POWERPC_EXCP_G2: + break; + default: + goto excp_invalid; + } + break; + case POWERPC_EXCP_MCHECK: /* Machine check exception */ + if (msr_me == 0) { + /* + * 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"); + } + cs->halted = 1; + cpu_interrupt_exittb(cs); + } + if (env->msr_mask & MSR_HVB) { + /* + * ISA specifies HV, but can be delivered to guest with HV + * clear (e.g., see FWNMI in PAPR). + */ + new_msr |= (target_ulong)MSR_HVB; + } + + /* machine check exceptions don't have ME set */ + new_msr &= ~((target_ulong)1 << MSR_ME); + + /* XXX: should also have something loaded in DAR / DSISR */ + switch (excp_model) { + case POWERPC_EXCP_40x: + srr0 = SPR_40x_SRR2; + srr1 = SPR_40x_SRR3; + break; + case POWERPC_EXCP_BOOKE: + /* FIXME: choose one or the other based on CPU type */ + srr0 = SPR_BOOKE_MCSRR0; + srr1 = SPR_BOOKE_MCSRR1; + + env->spr[SPR_BOOKE_CSRR0] = env->nip; + env->spr[SPR_BOOKE_CSRR1] = msr; + break; + default: + break; + } + break; + case POWERPC_EXCP_DSI: /* Data storage exception */ + trace_ppc_excp_dsi(env->spr[SPR_DSISR], env->spr[SPR_DAR]); + break; + case POWERPC_EXCP_ISI: /* Instruction storage exception */ + trace_ppc_excp_isi(msr, env->nip); + msr |= env->error_code; + break; + case POWERPC_EXCP_EXTERNAL: /* External input */ + { + bool lpes0; + + cs = CPU(cpu); + + /* + * Exception targeting modifiers + * + * LPES0 is supported on POWER7/8/9 + * LPES1 is not supported (old iSeries mode) + * + * On anything else, we behave as if LPES0 is 1 + * (externals don't alter MSR:HV) + */ +#if defined(TARGET_PPC64) + if (excp_model == POWERPC_EXCP_POWER7 || + excp_model == POWERPC_EXCP_POWER8 || + excp_model == POWERPC_EXCP_POWER9 || + excp_model == POWERPC_EXCP_POWER10) { + lpes0 = !!(env->spr[SPR_LPCR] & LPCR_LPES0); + } else +#endif /* defined(TARGET_PPC64) */ + { + lpes0 = true; + } + + if (!lpes0) { + new_msr |= (target_ulong)MSR_HVB; + new_msr |= env->msr & ((target_ulong)1 << MSR_RI); + srr0 = SPR_HSRR0; + srr1 = SPR_HSRR1; + } + if (env->mpic_proxy) { + /* IACK the IRQ on delivery */ + env->spr[SPR_BOOKE_EPR] = ldl_phys(cs->as, env->mpic_iack); + } + break; + } + case POWERPC_EXCP_ALIGN: /* Alignment exception */ + /* Get rS/rD and rA from faulting opcode */ + /* + * Note: the opcode fields will not be set properly for a + * direct store load/store, but nobody cares as nobody + * actually uses direct store segments. + */ + env->spr[SPR_DSISR] |= (env->error_code & 0x03FF0000) >> 16; + break; + case POWERPC_EXCP_PROGRAM: /* Program exception */ + switch (env->error_code & ~0xF) { + case POWERPC_EXCP_FP: + if ((msr_fe0 == 0 && msr_fe1 == 0) || msr_fp == 0) { + trace_ppc_excp_fp_ignore(); + cs->exception_index = POWERPC_EXCP_NONE; + env->error_code = 0; + return; + } + + /* + * FP exceptions always have NIP pointing to the faulting + * instruction, so always use store_next and claim we are + * precise in the MSR. + */ + msr |= 0x00100000; + env->spr[SPR_BOOKE_ESR] = ESR_FP; + break; + case POWERPC_EXCP_INVAL: + trace_ppc_excp_inval(env->nip); + msr |= 0x00080000; + env->spr[SPR_BOOKE_ESR] = ESR_PIL; + break; + case POWERPC_EXCP_PRIV: + msr |= 0x00040000; + env->spr[SPR_BOOKE_ESR] = ESR_PPR; + break; + case POWERPC_EXCP_TRAP: + msr |= 0x00020000; + env->spr[SPR_BOOKE_ESR] = ESR_PTR; + break; + default: + /* Should never occur */ + cpu_abort(cs, "Invalid program exception %d. Aborting\n", + env->error_code); + break; + } + break; + case POWERPC_EXCP_SYSCALL: /* System call exception */ + lev = env->error_code; + + if ((lev == 1) && cpu->vhyp) { + dump_hcall(env); + } else { + dump_syscall(env); + } + + /* + * We need to correct the NIP which in this case is supposed + * to point to the next instruction + */ + env->nip += 4; + + /* "PAPR mode" built-in hypercall emulation */ + if ((lev == 1) && cpu->vhyp) { + PPCVirtualHypervisorClass *vhc = + PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp); + vhc->hypercall(cpu->vhyp, cpu); + return; + } + if (lev == 1) { + new_msr |= (target_ulong)MSR_HVB; + } + break; + case POWERPC_EXCP_SYSCALL_VECTORED: /* scv exception */ + lev = env->error_code; + dump_syscall(env); + env->nip += 4; + new_msr |= env->msr & ((target_ulong)1 << MSR_EE); + new_msr |= env->msr & ((target_ulong)1 << MSR_RI); + + vector += lev * 0x20; + + env->lr = env->nip; + env->ctr = msr; + break; + case POWERPC_EXCP_FPU: /* Floating-point unavailable exception */ + case POWERPC_EXCP_APU: /* Auxiliary processor unavailable */ + case POWERPC_EXCP_DECR: /* Decrementer exception */ + break; + case POWERPC_EXCP_FIT: /* Fixed-interval timer interrupt */ + /* FIT on 4xx */ + trace_ppc_excp_print("FIT"); + break; + case POWERPC_EXCP_WDT: /* Watchdog timer interrupt */ + trace_ppc_excp_print("WDT"); + switch (excp_model) { + case POWERPC_EXCP_BOOKE: + srr0 = SPR_BOOKE_CSRR0; + srr1 = SPR_BOOKE_CSRR1; + break; + default: + break; + } + break; + case POWERPC_EXCP_DTLB: /* Data TLB error */ + case POWERPC_EXCP_ITLB: /* Instruction TLB error */ + break; + case POWERPC_EXCP_DEBUG: /* Debug interrupt */ + if (env->flags & POWERPC_FLAG_DE) { + /* FIXME: choose one or the other based on CPU type */ + srr0 = SPR_BOOKE_DSRR0; + srr1 = SPR_BOOKE_DSRR1; + + env->spr[SPR_BOOKE_CSRR0] = env->nip; + env->spr[SPR_BOOKE_CSRR1] = msr; + + /* DBSR already modified by caller */ + } else { + cpu_abort(cs, "Debug exception triggered on unsupported model\n"); + } + break; + case POWERPC_EXCP_SPEU: /* SPE/embedded floating-point unavailable/VPU */ + env->spr[SPR_BOOKE_ESR] = ESR_SPV; + break; + case POWERPC_EXCP_DOORI: /* Embedded doorbell interrupt */ + break; + case POWERPC_EXCP_DOORCI: /* Embedded doorbell critical interrupt */ + srr0 = SPR_BOOKE_CSRR0; + srr1 = SPR_BOOKE_CSRR1; + break; + case POWERPC_EXCP_RESET: /* System reset exception */ + /* A power-saving exception sets ME, otherwise it is unchanged */ + if (msr_pow) { + /* indicate that we resumed from power save mode */ + msr |= 0x10000; + new_msr |= ((target_ulong)1 << MSR_ME); + } + if (env->msr_mask & MSR_HVB) { + /* + * ISA specifies HV, but can be delivered to guest with HV + * clear (e.g., see FWNMI in PAPR, NMI injection in QEMU). + */ + new_msr |= (target_ulong)MSR_HVB; + } else { + if (msr_pow) { + cpu_abort(cs, "Trying to deliver power-saving system reset " + "exception %d with no HV support\n", excp); + } + } + break; + case POWERPC_EXCP_DSEG: /* Data segment exception */ + case POWERPC_EXCP_ISEG: /* Instruction segment exception */ + case POWERPC_EXCP_TRACE: /* Trace exception */ + break; + case POWERPC_EXCP_HISI: /* Hypervisor instruction storage exception */ + msr |= env->error_code; + /* fall through */ + case POWERPC_EXCP_HDECR: /* Hypervisor decrementer exception */ + case POWERPC_EXCP_HDSI: /* Hypervisor data storage exception */ + case POWERPC_EXCP_HDSEG: /* Hypervisor data segment exception */ + case POWERPC_EXCP_HISEG: /* Hypervisor instruction segment exception */ + case POWERPC_EXCP_SDOOR_HV: /* Hypervisor Doorbell interrupt */ + case POWERPC_EXCP_HV_EMU: + case POWERPC_EXCP_HVIRT: /* Hypervisor virtualization */ + srr0 = SPR_HSRR0; + srr1 = SPR_HSRR1; + new_msr |= (target_ulong)MSR_HVB; + new_msr |= env->msr & ((target_ulong)1 << MSR_RI); + break; + case POWERPC_EXCP_VPU: /* Vector unavailable exception */ + case POWERPC_EXCP_VSXU: /* VSX unavailable exception */ + case POWERPC_EXCP_FU: /* Facility unavailable exception */ +#ifdef TARGET_PPC64 + env->spr[SPR_FSCR] |= ((target_ulong)env->error_code << 56); +#endif + break; + case POWERPC_EXCP_HV_FU: /* Hypervisor Facility Unavailable Exception */ +#ifdef TARGET_PPC64 + env->spr[SPR_HFSCR] |= ((target_ulong)env->error_code << FSCR_IC_POS); + srr0 = SPR_HSRR0; + srr1 = SPR_HSRR1; + new_msr |= (target_ulong)MSR_HVB; + new_msr |= env->msr & ((target_ulong)1 << MSR_RI); +#endif + break; + case POWERPC_EXCP_PIT: /* Programmable interval timer interrupt */ + trace_ppc_excp_print("PIT"); + break; + case POWERPC_EXCP_IFTLB: /* Instruction fetch TLB error */ + case POWERPC_EXCP_DLTLB: /* Data load TLB miss */ + case POWERPC_EXCP_DSTLB: /* Data store TLB miss */ + switch (excp_model) { + case POWERPC_EXCP_602: + case POWERPC_EXCP_603: + case POWERPC_EXCP_G2: + /* Swap temporary saved registers with GPRs */ + if (!(new_msr & ((target_ulong)1 << MSR_TGPR))) { + new_msr |= (target_ulong)1 << MSR_TGPR; + hreg_swap_gpr_tgpr(env); + } + /* fall through */ + case POWERPC_EXCP_7x5: + ppc_excp_debug_sw_tlb(env, excp); + + msr |= env->crf[0] << 28; + msr |= env->error_code; /* key, D/I, S/L bits */ + /* Set way using a LRU mechanism */ + msr |= ((env->last_way + 1) & (env->nb_ways - 1)) << 17; + break; + default: + cpu_abort(cs, "Invalid TLB miss exception\n"); + break; + } + break; + case POWERPC_EXCP_EFPDI: /* Embedded floating-point data interrupt */ + case POWERPC_EXCP_EFPRI: /* Embedded floating-point round interrupt */ + case POWERPC_EXCP_EPERFM: /* Embedded performance monitor interrupt */ + case POWERPC_EXCP_IO: /* IO error exception */ + case POWERPC_EXCP_RUNM: /* Run mode exception */ + case POWERPC_EXCP_EMUL: /* Emulation trap exception */ + case POWERPC_EXCP_FPA: /* Floating-point assist exception */ + case POWERPC_EXCP_DABR: /* Data address breakpoint */ + case POWERPC_EXCP_IABR: /* Instruction address breakpoint */ + case POWERPC_EXCP_SMI: /* System management interrupt */ + case POWERPC_EXCP_THERM: /* Thermal interrupt */ + case POWERPC_EXCP_PERFM: /* Embedded performance monitor interrupt */ + case POWERPC_EXCP_VPUA: /* Vector assist exception */ + case POWERPC_EXCP_SOFTP: /* Soft patch exception */ + case POWERPC_EXCP_MAINT: /* Maintenance exception */ + case POWERPC_EXCP_MEXTBR: /* Maskable external breakpoint */ + case POWERPC_EXCP_NMEXTBR: /* Non maskable external breakpoint */ + cpu_abort(cs, "%s exception not implemented\n", + powerpc_excp_name(excp)); + break; + default: + excp_invalid: + cpu_abort(cs, "Invalid PowerPC exception %d. Aborting\n", excp); + break; + } + + /* Sanity check */ + if (!(env->msr_mask & MSR_HVB)) { + if (new_msr & MSR_HVB) { + cpu_abort(cs, "Trying to deliver HV exception (MSR) %d with " + "no HV support\n", excp); + } + if (srr0 == SPR_HSRR0) { + cpu_abort(cs, "Trying to deliver HV exception (HSRR) %d with " + "no HV support\n", excp); + } + } + + /* + * Sort out endianness of interrupt, this differs depending on the + * CPU, the HV mode, etc... + */ + if (ppc_interrupts_little_endian(cpu, !!(new_msr & MSR_HVB))) { + new_msr |= (target_ulong)1 << MSR_LE; + } + +#if defined(TARGET_PPC64) + if (excp_model == POWERPC_EXCP_BOOKE) { + if (env->spr[SPR_BOOKE_EPCR] & EPCR_ICM) { + /* Cat.64-bit: EPCR.ICM is copied to MSR.CM */ + new_msr |= (target_ulong)1 << MSR_CM; + } else { + vector = (uint32_t)vector; + } + } else { + if (!msr_isf && !mmu_is_64bit(env->mmu_model)) { + vector = (uint32_t)vector; + } else { + new_msr |= (target_ulong)1 << MSR_SF; + } + } +#endif + + if (excp != POWERPC_EXCP_SYSCALL_VECTORED) { + /* Save PC */ + env->spr[srr0] = env->nip; + + /* Save MSR */ + env->spr[srr1] = msr; + } + + /* This can update new_msr and vector if AIL applies */ + ppc_excp_apply_ail(cpu, excp_model, excp, msr, &new_msr, &vector); + + powerpc_set_excp_state(cpu, vector, new_msr); +} + /* * Note that this function should be greatly optimized when called * with a constant excp, from ppc_hw_interrupt @@ -1034,6 +1505,13 @@ static void powerpc_excp(PowerPCCPU *cpu, int excp) case POWERPC_EXCP_40x: powerpc_excp_40x(cpu, excp); break; + case POWERPC_EXCP_970: + case POWERPC_EXCP_POWER7: + case POWERPC_EXCP_POWER8: + case POWERPC_EXCP_POWER9: + case POWERPC_EXCP_POWER10: + powerpc_excp_books(cpu, excp); + break; default: powerpc_excp_legacy(cpu, excp); } From 30c4e4269c168187f4f681e7b87db5333fd0603e Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Fri, 28 Jan 2022 13:15:05 +0100 Subject: [PATCH 060/460] target/ppc: Simplify powerpc_excp_books MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Differences from the generic powerpc_excp code: - Not BookE, so some MSR bits are cleared at interrupt dispatch; - Always uses HV_EMU if the CPU has MSR_HV; - Exceptions always delivered in 64 bit. Exceptions used: POWERPC_EXCP_ALIGN POWERPC_EXCP_DECR POWERPC_EXCP_DSEG POWERPC_EXCP_DSI POWERPC_EXCP_EXTERNAL POWERPC_EXCP_FPU POWERPC_EXCP_FU POWERPC_EXCP_HDECR POWERPC_EXCP_HDSI POWERPC_EXCP_HISI POWERPC_EXCP_HVIRT POWERPC_EXCP_HV_EMU POWERPC_EXCP_HV_FU POWERPC_EXCP_ISEG POWERPC_EXCP_ISI POWERPC_EXCP_MAINT POWERPC_EXCP_MCHECK POWERPC_EXCP_PERFM POWERPC_EXCP_PROGRAM POWERPC_EXCP_RESET POWERPC_EXCP_SDOOR_HV POWERPC_EXCP_SYSCALL POWERPC_EXCP_SYSCALL_VECTORED POWERPC_EXCP_THERM POWERPC_EXCP_TRACE POWERPC_EXCP_VPU POWERPC_EXCP_VPUA POWERPC_EXCP_VSXU POWERPC_EXCP_HV_MAINT POWERPC_EXCP_SDOOR (I added the two above that were not being considered. They used to be "Invalid exception". Now they become "Unimplemented exception" which is more accurate.) Signed-off-by: Fabiano Rosas Reviewed-by: Cédric Le Goater Message-Id: <20220124184605.999353-3-farosas@linux.ibm.com> Signed-off-by: Cédric Le Goater --- target/ppc/excp_helper.c | 161 ++++----------------------------------- 1 file changed, 14 insertions(+), 147 deletions(-) diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index a2a605f8f5..161b207bc7 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -551,6 +551,7 @@ static void powerpc_excp_40x(PowerPCCPU *cpu, int excp) powerpc_set_excp_state(cpu, vector, new_msr); } +#ifdef TARGET_PPC64 static void powerpc_excp_books(PowerPCCPU *cpu, int excp) { CPUState *cs = CPU(cpu); @@ -568,11 +569,7 @@ static void powerpc_excp_books(PowerPCCPU *cpu, int excp) excp, env->error_code); /* new srr1 value excluding must-be-zero bits */ - if (excp_model == POWERPC_EXCP_BOOKE) { - msr = env->msr; - } else { - msr = env->msr & ~0x783f0000ULL; - } + msr = env->msr & ~0x783f0000ULL; /* * new interrupt handler msr preserves existing HV and ME unless @@ -593,29 +590,13 @@ static void powerpc_excp_books(PowerPCCPU *cpu, int excp) } /* - * Hypervisor emulation assistance interrupt only exists on server - * arch 2.05 server or later. We also don't want to generate it if - * we don't have HVB in msr_mask (PAPR mode). + * We don't want to generate a Hypervisor Emulation Assistance + * Interrupt if we don't have HVB in msr_mask (PAPR mode). */ - if (excp == POWERPC_EXCP_HV_EMU -#if defined(TARGET_PPC64) - && !(mmu_is_64bit(env->mmu_model) && (env->msr_mask & MSR_HVB)) -#endif /* defined(TARGET_PPC64) */ - - ) { + if (excp == POWERPC_EXCP_HV_EMU && !(env->msr_mask & MSR_HVB)) { excp = POWERPC_EXCP_PROGRAM; } -#ifdef TARGET_PPC64 - /* - * SPEU and VPU share the same IVOR but they exist in different - * processors. SPEU is e500v1/2 only and VPU is e6500 only. - */ - if (excp_model == POWERPC_EXCP_BOOKE && excp == POWERPC_EXCP_VPU) { - excp = POWERPC_EXCP_SPEU; - } -#endif - vector = env->excp_vectors[excp]; if (vector == (target_ulong)-1ULL) { cpu_abort(cs, "Raised an exception without defined vector %d\n", @@ -625,22 +606,6 @@ static void powerpc_excp_books(PowerPCCPU *cpu, int excp) vector |= env->excp_prefix; switch (excp) { - case POWERPC_EXCP_CRITICAL: /* Critical input */ - switch (excp_model) { - case POWERPC_EXCP_40x: - srr0 = SPR_40x_SRR2; - srr1 = SPR_40x_SRR3; - break; - case POWERPC_EXCP_BOOKE: - srr0 = SPR_BOOKE_CSRR0; - srr1 = SPR_BOOKE_CSRR1; - break; - case POWERPC_EXCP_G2: - break; - default: - goto excp_invalid; - } - break; case POWERPC_EXCP_MCHECK: /* Machine check exception */ if (msr_me == 0) { /* @@ -817,50 +782,8 @@ static void powerpc_excp_books(PowerPCCPU *cpu, int excp) env->ctr = msr; break; case POWERPC_EXCP_FPU: /* Floating-point unavailable exception */ - case POWERPC_EXCP_APU: /* Auxiliary processor unavailable */ case POWERPC_EXCP_DECR: /* Decrementer exception */ break; - case POWERPC_EXCP_FIT: /* Fixed-interval timer interrupt */ - /* FIT on 4xx */ - trace_ppc_excp_print("FIT"); - break; - case POWERPC_EXCP_WDT: /* Watchdog timer interrupt */ - trace_ppc_excp_print("WDT"); - switch (excp_model) { - case POWERPC_EXCP_BOOKE: - srr0 = SPR_BOOKE_CSRR0; - srr1 = SPR_BOOKE_CSRR1; - break; - default: - break; - } - break; - case POWERPC_EXCP_DTLB: /* Data TLB error */ - case POWERPC_EXCP_ITLB: /* Instruction TLB error */ - break; - case POWERPC_EXCP_DEBUG: /* Debug interrupt */ - if (env->flags & POWERPC_FLAG_DE) { - /* FIXME: choose one or the other based on CPU type */ - srr0 = SPR_BOOKE_DSRR0; - srr1 = SPR_BOOKE_DSRR1; - - env->spr[SPR_BOOKE_CSRR0] = env->nip; - env->spr[SPR_BOOKE_CSRR1] = msr; - - /* DBSR already modified by caller */ - } else { - cpu_abort(cs, "Debug exception triggered on unsupported model\n"); - } - break; - case POWERPC_EXCP_SPEU: /* SPE/embedded floating-point unavailable/VPU */ - env->spr[SPR_BOOKE_ESR] = ESR_SPV; - break; - case POWERPC_EXCP_DOORI: /* Embedded doorbell interrupt */ - break; - case POWERPC_EXCP_DOORCI: /* Embedded doorbell critical interrupt */ - srr0 = SPR_BOOKE_CSRR0; - srr1 = SPR_BOOKE_CSRR1; - break; case POWERPC_EXCP_RESET: /* System reset exception */ /* A power-saving exception sets ME, otherwise it is unchanged */ if (msr_pow) { @@ -890,8 +813,6 @@ static void powerpc_excp_books(PowerPCCPU *cpu, int excp) /* fall through */ case POWERPC_EXCP_HDECR: /* Hypervisor decrementer exception */ case POWERPC_EXCP_HDSI: /* Hypervisor data storage exception */ - case POWERPC_EXCP_HDSEG: /* Hypervisor data segment exception */ - case POWERPC_EXCP_HISEG: /* Hypervisor instruction segment exception */ case POWERPC_EXCP_SDOOR_HV: /* Hypervisor Doorbell interrupt */ case POWERPC_EXCP_HV_EMU: case POWERPC_EXCP_HVIRT: /* Hypervisor virtualization */ @@ -903,70 +824,25 @@ static void powerpc_excp_books(PowerPCCPU *cpu, int excp) case POWERPC_EXCP_VPU: /* Vector unavailable exception */ case POWERPC_EXCP_VSXU: /* VSX unavailable exception */ case POWERPC_EXCP_FU: /* Facility unavailable exception */ -#ifdef TARGET_PPC64 env->spr[SPR_FSCR] |= ((target_ulong)env->error_code << 56); -#endif break; case POWERPC_EXCP_HV_FU: /* Hypervisor Facility Unavailable Exception */ -#ifdef TARGET_PPC64 env->spr[SPR_HFSCR] |= ((target_ulong)env->error_code << FSCR_IC_POS); srr0 = SPR_HSRR0; srr1 = SPR_HSRR1; new_msr |= (target_ulong)MSR_HVB; new_msr |= env->msr & ((target_ulong)1 << MSR_RI); -#endif break; - case POWERPC_EXCP_PIT: /* Programmable interval timer interrupt */ - trace_ppc_excp_print("PIT"); - break; - case POWERPC_EXCP_IFTLB: /* Instruction fetch TLB error */ - case POWERPC_EXCP_DLTLB: /* Data load TLB miss */ - case POWERPC_EXCP_DSTLB: /* Data store TLB miss */ - switch (excp_model) { - case POWERPC_EXCP_602: - case POWERPC_EXCP_603: - case POWERPC_EXCP_G2: - /* Swap temporary saved registers with GPRs */ - if (!(new_msr & ((target_ulong)1 << MSR_TGPR))) { - new_msr |= (target_ulong)1 << MSR_TGPR; - hreg_swap_gpr_tgpr(env); - } - /* fall through */ - case POWERPC_EXCP_7x5: - ppc_excp_debug_sw_tlb(env, excp); - - msr |= env->crf[0] << 28; - msr |= env->error_code; /* key, D/I, S/L bits */ - /* Set way using a LRU mechanism */ - msr |= ((env->last_way + 1) & (env->nb_ways - 1)) << 17; - break; - default: - cpu_abort(cs, "Invalid TLB miss exception\n"); - break; - } - break; - case POWERPC_EXCP_EFPDI: /* Embedded floating-point data interrupt */ - case POWERPC_EXCP_EFPRI: /* Embedded floating-point round interrupt */ - case POWERPC_EXCP_EPERFM: /* Embedded performance monitor interrupt */ - case POWERPC_EXCP_IO: /* IO error exception */ - case POWERPC_EXCP_RUNM: /* Run mode exception */ - case POWERPC_EXCP_EMUL: /* Emulation trap exception */ - case POWERPC_EXCP_FPA: /* Floating-point assist exception */ - case POWERPC_EXCP_DABR: /* Data address breakpoint */ - case POWERPC_EXCP_IABR: /* Instruction address breakpoint */ - case POWERPC_EXCP_SMI: /* System management interrupt */ case POWERPC_EXCP_THERM: /* Thermal interrupt */ case POWERPC_EXCP_PERFM: /* Embedded performance monitor interrupt */ case POWERPC_EXCP_VPUA: /* Vector assist exception */ - case POWERPC_EXCP_SOFTP: /* Soft patch exception */ case POWERPC_EXCP_MAINT: /* Maintenance exception */ - case POWERPC_EXCP_MEXTBR: /* Maskable external breakpoint */ - case POWERPC_EXCP_NMEXTBR: /* Non maskable external breakpoint */ + case POWERPC_EXCP_SDOOR: /* Doorbell interrupt */ + case POWERPC_EXCP_HV_MAINT: /* Hypervisor Maintenance exception */ cpu_abort(cs, "%s exception not implemented\n", powerpc_excp_name(excp)); break; default: - excp_invalid: cpu_abort(cs, "Invalid PowerPC exception %d. Aborting\n", excp); break; } @@ -991,22 +867,7 @@ static void powerpc_excp_books(PowerPCCPU *cpu, int excp) new_msr |= (target_ulong)1 << MSR_LE; } -#if defined(TARGET_PPC64) - if (excp_model == POWERPC_EXCP_BOOKE) { - if (env->spr[SPR_BOOKE_EPCR] & EPCR_ICM) { - /* Cat.64-bit: EPCR.ICM is copied to MSR.CM */ - new_msr |= (target_ulong)1 << MSR_CM; - } else { - vector = (uint32_t)vector; - } - } else { - if (!msr_isf && !mmu_is_64bit(env->mmu_model)) { - vector = (uint32_t)vector; - } else { - new_msr |= (target_ulong)1 << MSR_SF; - } - } -#endif + new_msr |= (target_ulong)1 << MSR_SF; if (excp != POWERPC_EXCP_SYSCALL_VECTORED) { /* Save PC */ @@ -1021,6 +882,12 @@ static void powerpc_excp_books(PowerPCCPU *cpu, int excp) powerpc_set_excp_state(cpu, vector, new_msr); } +#else +static inline void powerpc_excp_books(PowerPCCPU *cpu, int excp) +{ + g_assert_not_reached(); +} +#endif /* * Note that this function should be greatly optimized when called From 58a02119f39581409444c31e04d6b8b2c15e6f8e Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Fri, 28 Jan 2022 13:15:05 +0100 Subject: [PATCH 061/460] target/ppc: books: Machine Check exception cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit powerpc_excp_books is BookS only, so remove 40x and BookE code. Signed-off-by: Fabiano Rosas Reviewed-by: Cédric Le Goater Message-Id: <20220124184605.999353-4-farosas@linux.ibm.com> Signed-off-by: Cédric Le Goater --- target/ppc/excp_helper.c | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index 161b207bc7..be8c64a0cd 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -632,23 +632,6 @@ static void powerpc_excp_books(PowerPCCPU *cpu, int excp) /* machine check exceptions don't have ME set */ new_msr &= ~((target_ulong)1 << MSR_ME); - /* XXX: should also have something loaded in DAR / DSISR */ - switch (excp_model) { - case POWERPC_EXCP_40x: - srr0 = SPR_40x_SRR2; - srr1 = SPR_40x_SRR3; - break; - case POWERPC_EXCP_BOOKE: - /* FIXME: choose one or the other based on CPU type */ - srr0 = SPR_BOOKE_MCSRR0; - srr1 = SPR_BOOKE_MCSRR1; - - env->spr[SPR_BOOKE_CSRR0] = env->nip; - env->spr[SPR_BOOKE_CSRR1] = msr; - break; - default: - break; - } break; case POWERPC_EXCP_DSI: /* Data storage exception */ trace_ppc_excp_dsi(env->spr[SPR_DSISR], env->spr[SPR_DAR]); From 67baff7715b3c1a2beb7df7af615eb3f132b9d13 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Fri, 28 Jan 2022 13:15:06 +0100 Subject: [PATCH 062/460] target/ppc: books: External interrupt cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since this is now BookS only, we can simplify the code a bit and check has_hv_mode instead of enumerating the exception models. LPES0 does not make sense if there is no MSR_HV. Note that QEMU does not support HV mode on 970 and POWER5+ so we don't set MSR_HV in msr_mask. Signed-off-by: Fabiano Rosas Reviewed-by: Cédric Le Goater Message-Id: <20220124184605.999353-5-farosas@linux.ibm.com> Signed-off-by: Cédric Le Goater --- target/ppc/excp_helper.c | 30 +++++++----------------------- 1 file changed, 7 insertions(+), 23 deletions(-) diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index be8c64a0cd..e0d6287c4e 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -644,39 +644,23 @@ static void powerpc_excp_books(PowerPCCPU *cpu, int excp) { bool lpes0; - cs = CPU(cpu); - /* - * Exception targeting modifiers - * - * LPES0 is supported on POWER7/8/9 - * LPES1 is not supported (old iSeries mode) - * - * On anything else, we behave as if LPES0 is 1 - * (externals don't alter MSR:HV) + * LPES0 is only taken into consideration if we support HV + * mode for this CPU. */ -#if defined(TARGET_PPC64) - if (excp_model == POWERPC_EXCP_POWER7 || - excp_model == POWERPC_EXCP_POWER8 || - excp_model == POWERPC_EXCP_POWER9 || - excp_model == POWERPC_EXCP_POWER10) { - lpes0 = !!(env->spr[SPR_LPCR] & LPCR_LPES0); - } else -#endif /* defined(TARGET_PPC64) */ - { - lpes0 = true; + if (!env->has_hv_mode) { + break; } + lpes0 = !!(env->spr[SPR_LPCR] & LPCR_LPES0); + if (!lpes0) { new_msr |= (target_ulong)MSR_HVB; new_msr |= env->msr & ((target_ulong)1 << MSR_RI); srr0 = SPR_HSRR0; srr1 = SPR_HSRR1; } - if (env->mpic_proxy) { - /* IACK the IRQ on delivery */ - env->spr[SPR_BOOKE_EPR] = ldl_phys(cs->as, env->mpic_iack); - } + break; } case POWERPC_EXCP_ALIGN: /* Alignment exception */ From aca2b93fd7edd00d70bf51bf01428e8f2b6456e0 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Fri, 28 Jan 2022 13:15:06 +0100 Subject: [PATCH 063/460] target/ppc: books: Program exception cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove setting of BookE registers. Signed-off-by: Fabiano Rosas Reviewed-by: Cédric Le Goater Message-Id: <20220124184605.999353-6-farosas@linux.ibm.com> Signed-off-by: Cédric Le Goater --- target/ppc/excp_helper.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index e0d6287c4e..0e84cecc68 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -688,20 +688,16 @@ static void powerpc_excp_books(PowerPCCPU *cpu, int excp) * precise in the MSR. */ msr |= 0x00100000; - env->spr[SPR_BOOKE_ESR] = ESR_FP; break; case POWERPC_EXCP_INVAL: trace_ppc_excp_inval(env->nip); msr |= 0x00080000; - env->spr[SPR_BOOKE_ESR] = ESR_PIL; break; case POWERPC_EXCP_PRIV: msr |= 0x00040000; - env->spr[SPR_BOOKE_ESR] = ESR_PPR; break; case POWERPC_EXCP_TRAP: msr |= 0x00020000; - env->spr[SPR_BOOKE_ESR] = ESR_PTR; break; default: /* Should never occur */ From 52926b0debd4aeced56c1560a4ff77b56547df22 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Fri, 28 Jan 2022 13:15:06 +0100 Subject: [PATCH 064/460] target/ppc: Introduce powerpc_excp_74xx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce a new powerpc_excp function specific for PowerPC 74xx CPUs. This commit copies powerpc_excp_legacy verbatim so the next one has a clean diff. Signed-off-by: Fabiano Rosas Message-Id: <20220127201116.1154733-2-farosas@linux.ibm.com> Signed-off-by: Cédric Le Goater --- target/ppc/excp_helper.c | 474 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 474 insertions(+) diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index 0e84cecc68..0e3d3ffcf4 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -551,6 +551,477 @@ static void powerpc_excp_40x(PowerPCCPU *cpu, int excp) powerpc_set_excp_state(cpu, vector, new_msr); } +static void powerpc_excp_74xx(PowerPCCPU *cpu, int excp) +{ + CPUState *cs = CPU(cpu); + CPUPPCState *env = &cpu->env; + int excp_model = env->excp_model; + target_ulong msr, new_msr, vector; + int srr0, srr1, lev = -1; + + if (excp <= POWERPC_EXCP_NONE || excp >= POWERPC_EXCP_NB) { + cpu_abort(cs, "Invalid PowerPC exception %d. Aborting\n", excp); + } + + qemu_log_mask(CPU_LOG_INT, "Raise exception at " TARGET_FMT_lx + " => %s (%d) error=%02x\n", env->nip, powerpc_excp_name(excp), + excp, env->error_code); + + /* new srr1 value excluding must-be-zero bits */ + if (excp_model == POWERPC_EXCP_BOOKE) { + msr = env->msr; + } else { + msr = env->msr & ~0x783f0000ULL; + } + + /* + * new interrupt handler msr preserves existing HV and ME unless + * explicitly overriden + */ + new_msr = env->msr & (((target_ulong)1 << MSR_ME) | MSR_HVB); + + /* target registers */ + srr0 = SPR_SRR0; + srr1 = SPR_SRR1; + + /* + * check for special resume at 0x100 from doze/nap/sleep/winkle on + * P7/P8/P9 + */ + if (env->resume_as_sreset) { + excp = powerpc_reset_wakeup(cs, env, excp, &msr); + } + + /* + * Hypervisor emulation assistance interrupt only exists on server + * arch 2.05 server or later. We also don't want to generate it if + * we don't have HVB in msr_mask (PAPR mode). + */ + if (excp == POWERPC_EXCP_HV_EMU +#if defined(TARGET_PPC64) + && !(mmu_is_64bit(env->mmu_model) && (env->msr_mask & MSR_HVB)) +#endif /* defined(TARGET_PPC64) */ + + ) { + excp = POWERPC_EXCP_PROGRAM; + } + +#ifdef TARGET_PPC64 + /* + * SPEU and VPU share the same IVOR but they exist in different + * processors. SPEU is e500v1/2 only and VPU is e6500 only. + */ + if (excp_model == POWERPC_EXCP_BOOKE && excp == POWERPC_EXCP_VPU) { + excp = POWERPC_EXCP_SPEU; + } +#endif + + vector = env->excp_vectors[excp]; + if (vector == (target_ulong)-1ULL) { + cpu_abort(cs, "Raised an exception without defined vector %d\n", + excp); + } + + vector |= env->excp_prefix; + + switch (excp) { + case POWERPC_EXCP_CRITICAL: /* Critical input */ + switch (excp_model) { + case POWERPC_EXCP_40x: + srr0 = SPR_40x_SRR2; + srr1 = SPR_40x_SRR3; + break; + case POWERPC_EXCP_BOOKE: + srr0 = SPR_BOOKE_CSRR0; + srr1 = SPR_BOOKE_CSRR1; + break; + case POWERPC_EXCP_G2: + break; + default: + goto excp_invalid; + } + break; + case POWERPC_EXCP_MCHECK: /* Machine check exception */ + if (msr_me == 0) { + /* + * 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"); + } + cs->halted = 1; + cpu_interrupt_exittb(cs); + } + if (env->msr_mask & MSR_HVB) { + /* + * ISA specifies HV, but can be delivered to guest with HV + * clear (e.g., see FWNMI in PAPR). + */ + new_msr |= (target_ulong)MSR_HVB; + } + + /* machine check exceptions don't have ME set */ + new_msr &= ~((target_ulong)1 << MSR_ME); + + /* XXX: should also have something loaded in DAR / DSISR */ + switch (excp_model) { + case POWERPC_EXCP_40x: + srr0 = SPR_40x_SRR2; + srr1 = SPR_40x_SRR3; + break; + case POWERPC_EXCP_BOOKE: + /* FIXME: choose one or the other based on CPU type */ + srr0 = SPR_BOOKE_MCSRR0; + srr1 = SPR_BOOKE_MCSRR1; + + env->spr[SPR_BOOKE_CSRR0] = env->nip; + env->spr[SPR_BOOKE_CSRR1] = msr; + break; + default: + break; + } + break; + case POWERPC_EXCP_DSI: /* Data storage exception */ + trace_ppc_excp_dsi(env->spr[SPR_DSISR], env->spr[SPR_DAR]); + break; + case POWERPC_EXCP_ISI: /* Instruction storage exception */ + trace_ppc_excp_isi(msr, env->nip); + msr |= env->error_code; + break; + case POWERPC_EXCP_EXTERNAL: /* External input */ + { + bool lpes0; + + cs = CPU(cpu); + + /* + * Exception targeting modifiers + * + * LPES0 is supported on POWER7/8/9 + * LPES1 is not supported (old iSeries mode) + * + * On anything else, we behave as if LPES0 is 1 + * (externals don't alter MSR:HV) + */ +#if defined(TARGET_PPC64) + if (excp_model == POWERPC_EXCP_POWER7 || + excp_model == POWERPC_EXCP_POWER8 || + excp_model == POWERPC_EXCP_POWER9 || + excp_model == POWERPC_EXCP_POWER10) { + lpes0 = !!(env->spr[SPR_LPCR] & LPCR_LPES0); + } else +#endif /* defined(TARGET_PPC64) */ + { + lpes0 = true; + } + + if (!lpes0) { + new_msr |= (target_ulong)MSR_HVB; + new_msr |= env->msr & ((target_ulong)1 << MSR_RI); + srr0 = SPR_HSRR0; + srr1 = SPR_HSRR1; + } + if (env->mpic_proxy) { + /* IACK the IRQ on delivery */ + env->spr[SPR_BOOKE_EPR] = ldl_phys(cs->as, env->mpic_iack); + } + break; + } + case POWERPC_EXCP_ALIGN: /* Alignment exception */ + /* Get rS/rD and rA from faulting opcode */ + /* + * Note: the opcode fields will not be set properly for a + * direct store load/store, but nobody cares as nobody + * actually uses direct store segments. + */ + env->spr[SPR_DSISR] |= (env->error_code & 0x03FF0000) >> 16; + break; + case POWERPC_EXCP_PROGRAM: /* Program exception */ + switch (env->error_code & ~0xF) { + case POWERPC_EXCP_FP: + if ((msr_fe0 == 0 && msr_fe1 == 0) || msr_fp == 0) { + trace_ppc_excp_fp_ignore(); + cs->exception_index = POWERPC_EXCP_NONE; + env->error_code = 0; + return; + } + + /* + * FP exceptions always have NIP pointing to the faulting + * instruction, so always use store_next and claim we are + * precise in the MSR. + */ + msr |= 0x00100000; + env->spr[SPR_BOOKE_ESR] = ESR_FP; + break; + case POWERPC_EXCP_INVAL: + trace_ppc_excp_inval(env->nip); + msr |= 0x00080000; + env->spr[SPR_BOOKE_ESR] = ESR_PIL; + break; + case POWERPC_EXCP_PRIV: + msr |= 0x00040000; + env->spr[SPR_BOOKE_ESR] = ESR_PPR; + break; + case POWERPC_EXCP_TRAP: + msr |= 0x00020000; + env->spr[SPR_BOOKE_ESR] = ESR_PTR; + break; + default: + /* Should never occur */ + cpu_abort(cs, "Invalid program exception %d. Aborting\n", + env->error_code); + break; + } + break; + case POWERPC_EXCP_SYSCALL: /* System call exception */ + lev = env->error_code; + + if ((lev == 1) && cpu->vhyp) { + dump_hcall(env); + } else { + dump_syscall(env); + } + + /* + * We need to correct the NIP which in this case is supposed + * to point to the next instruction + */ + env->nip += 4; + + /* "PAPR mode" built-in hypercall emulation */ + if ((lev == 1) && cpu->vhyp) { + PPCVirtualHypervisorClass *vhc = + PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp); + vhc->hypercall(cpu->vhyp, cpu); + return; + } + if (lev == 1) { + new_msr |= (target_ulong)MSR_HVB; + } + break; + case POWERPC_EXCP_SYSCALL_VECTORED: /* scv exception */ + lev = env->error_code; + dump_syscall(env); + env->nip += 4; + new_msr |= env->msr & ((target_ulong)1 << MSR_EE); + new_msr |= env->msr & ((target_ulong)1 << MSR_RI); + + vector += lev * 0x20; + + env->lr = env->nip; + env->ctr = msr; + break; + case POWERPC_EXCP_FPU: /* Floating-point unavailable exception */ + case POWERPC_EXCP_APU: /* Auxiliary processor unavailable */ + case POWERPC_EXCP_DECR: /* Decrementer exception */ + break; + case POWERPC_EXCP_FIT: /* Fixed-interval timer interrupt */ + /* FIT on 4xx */ + trace_ppc_excp_print("FIT"); + break; + case POWERPC_EXCP_WDT: /* Watchdog timer interrupt */ + trace_ppc_excp_print("WDT"); + switch (excp_model) { + case POWERPC_EXCP_BOOKE: + srr0 = SPR_BOOKE_CSRR0; + srr1 = SPR_BOOKE_CSRR1; + break; + default: + break; + } + break; + case POWERPC_EXCP_DTLB: /* Data TLB error */ + case POWERPC_EXCP_ITLB: /* Instruction TLB error */ + break; + case POWERPC_EXCP_DEBUG: /* Debug interrupt */ + if (env->flags & POWERPC_FLAG_DE) { + /* FIXME: choose one or the other based on CPU type */ + srr0 = SPR_BOOKE_DSRR0; + srr1 = SPR_BOOKE_DSRR1; + + env->spr[SPR_BOOKE_CSRR0] = env->nip; + env->spr[SPR_BOOKE_CSRR1] = msr; + + /* DBSR already modified by caller */ + } else { + cpu_abort(cs, "Debug exception triggered on unsupported model\n"); + } + break; + case POWERPC_EXCP_SPEU: /* SPE/embedded floating-point unavailable/VPU */ + env->spr[SPR_BOOKE_ESR] = ESR_SPV; + break; + case POWERPC_EXCP_DOORI: /* Embedded doorbell interrupt */ + break; + case POWERPC_EXCP_DOORCI: /* Embedded doorbell critical interrupt */ + srr0 = SPR_BOOKE_CSRR0; + srr1 = SPR_BOOKE_CSRR1; + break; + case POWERPC_EXCP_RESET: /* System reset exception */ + /* A power-saving exception sets ME, otherwise it is unchanged */ + if (msr_pow) { + /* indicate that we resumed from power save mode */ + msr |= 0x10000; + new_msr |= ((target_ulong)1 << MSR_ME); + } + if (env->msr_mask & MSR_HVB) { + /* + * ISA specifies HV, but can be delivered to guest with HV + * clear (e.g., see FWNMI in PAPR, NMI injection in QEMU). + */ + new_msr |= (target_ulong)MSR_HVB; + } else { + if (msr_pow) { + cpu_abort(cs, "Trying to deliver power-saving system reset " + "exception %d with no HV support\n", excp); + } + } + break; + case POWERPC_EXCP_DSEG: /* Data segment exception */ + case POWERPC_EXCP_ISEG: /* Instruction segment exception */ + case POWERPC_EXCP_TRACE: /* Trace exception */ + break; + case POWERPC_EXCP_HISI: /* Hypervisor instruction storage exception */ + msr |= env->error_code; + /* fall through */ + case POWERPC_EXCP_HDECR: /* Hypervisor decrementer exception */ + case POWERPC_EXCP_HDSI: /* Hypervisor data storage exception */ + case POWERPC_EXCP_HDSEG: /* Hypervisor data segment exception */ + case POWERPC_EXCP_HISEG: /* Hypervisor instruction segment exception */ + case POWERPC_EXCP_SDOOR_HV: /* Hypervisor Doorbell interrupt */ + case POWERPC_EXCP_HV_EMU: + case POWERPC_EXCP_HVIRT: /* Hypervisor virtualization */ + srr0 = SPR_HSRR0; + srr1 = SPR_HSRR1; + new_msr |= (target_ulong)MSR_HVB; + new_msr |= env->msr & ((target_ulong)1 << MSR_RI); + break; + case POWERPC_EXCP_VPU: /* Vector unavailable exception */ + case POWERPC_EXCP_VSXU: /* VSX unavailable exception */ + case POWERPC_EXCP_FU: /* Facility unavailable exception */ +#ifdef TARGET_PPC64 + env->spr[SPR_FSCR] |= ((target_ulong)env->error_code << 56); +#endif + break; + case POWERPC_EXCP_HV_FU: /* Hypervisor Facility Unavailable Exception */ +#ifdef TARGET_PPC64 + env->spr[SPR_HFSCR] |= ((target_ulong)env->error_code << FSCR_IC_POS); + srr0 = SPR_HSRR0; + srr1 = SPR_HSRR1; + new_msr |= (target_ulong)MSR_HVB; + new_msr |= env->msr & ((target_ulong)1 << MSR_RI); +#endif + break; + case POWERPC_EXCP_PIT: /* Programmable interval timer interrupt */ + trace_ppc_excp_print("PIT"); + break; + case POWERPC_EXCP_IFTLB: /* Instruction fetch TLB error */ + case POWERPC_EXCP_DLTLB: /* Data load TLB miss */ + case POWERPC_EXCP_DSTLB: /* Data store TLB miss */ + switch (excp_model) { + case POWERPC_EXCP_602: + case POWERPC_EXCP_603: + case POWERPC_EXCP_G2: + /* Swap temporary saved registers with GPRs */ + if (!(new_msr & ((target_ulong)1 << MSR_TGPR))) { + new_msr |= (target_ulong)1 << MSR_TGPR; + hreg_swap_gpr_tgpr(env); + } + /* fall through */ + case POWERPC_EXCP_7x5: + ppc_excp_debug_sw_tlb(env, excp); + + msr |= env->crf[0] << 28; + msr |= env->error_code; /* key, D/I, S/L bits */ + /* Set way using a LRU mechanism */ + msr |= ((env->last_way + 1) & (env->nb_ways - 1)) << 17; + break; + default: + cpu_abort(cs, "Invalid TLB miss exception\n"); + break; + } + break; + case POWERPC_EXCP_EFPDI: /* Embedded floating-point data interrupt */ + case POWERPC_EXCP_EFPRI: /* Embedded floating-point round interrupt */ + case POWERPC_EXCP_EPERFM: /* Embedded performance monitor interrupt */ + case POWERPC_EXCP_IO: /* IO error exception */ + case POWERPC_EXCP_RUNM: /* Run mode exception */ + case POWERPC_EXCP_EMUL: /* Emulation trap exception */ + case POWERPC_EXCP_FPA: /* Floating-point assist exception */ + case POWERPC_EXCP_DABR: /* Data address breakpoint */ + case POWERPC_EXCP_IABR: /* Instruction address breakpoint */ + case POWERPC_EXCP_SMI: /* System management interrupt */ + case POWERPC_EXCP_THERM: /* Thermal interrupt */ + case POWERPC_EXCP_PERFM: /* Embedded performance monitor interrupt */ + case POWERPC_EXCP_VPUA: /* Vector assist exception */ + case POWERPC_EXCP_SOFTP: /* Soft patch exception */ + case POWERPC_EXCP_MAINT: /* Maintenance exception */ + case POWERPC_EXCP_MEXTBR: /* Maskable external breakpoint */ + case POWERPC_EXCP_NMEXTBR: /* Non maskable external breakpoint */ + cpu_abort(cs, "%s exception not implemented\n", + powerpc_excp_name(excp)); + break; + default: + excp_invalid: + cpu_abort(cs, "Invalid PowerPC exception %d. Aborting\n", excp); + break; + } + + /* Sanity check */ + if (!(env->msr_mask & MSR_HVB)) { + if (new_msr & MSR_HVB) { + cpu_abort(cs, "Trying to deliver HV exception (MSR) %d with " + "no HV support\n", excp); + } + if (srr0 == SPR_HSRR0) { + cpu_abort(cs, "Trying to deliver HV exception (HSRR) %d with " + "no HV support\n", excp); + } + } + + /* + * Sort out endianness of interrupt, this differs depending on the + * CPU, the HV mode, etc... + */ + if (ppc_interrupts_little_endian(cpu, !!(new_msr & MSR_HVB))) { + new_msr |= (target_ulong)1 << MSR_LE; + } + +#if defined(TARGET_PPC64) + if (excp_model == POWERPC_EXCP_BOOKE) { + if (env->spr[SPR_BOOKE_EPCR] & EPCR_ICM) { + /* Cat.64-bit: EPCR.ICM is copied to MSR.CM */ + new_msr |= (target_ulong)1 << MSR_CM; + } else { + vector = (uint32_t)vector; + } + } else { + if (!msr_isf && !mmu_is_64bit(env->mmu_model)) { + vector = (uint32_t)vector; + } else { + new_msr |= (target_ulong)1 << MSR_SF; + } + } +#endif + + if (excp != POWERPC_EXCP_SYSCALL_VECTORED) { + /* Save PC */ + env->spr[srr0] = env->nip; + + /* Save MSR */ + env->spr[srr1] = msr; + } + + /* This can update new_msr and vector if AIL applies */ + ppc_excp_apply_ail(cpu, excp_model, excp, msr, &new_msr, &vector); + + powerpc_set_excp_state(cpu, vector, new_msr); +} + #ifdef TARGET_PPC64 static void powerpc_excp_books(PowerPCCPU *cpu, int excp) { @@ -1335,6 +1806,9 @@ static void powerpc_excp(PowerPCCPU *cpu, int excp) case POWERPC_EXCP_40x: powerpc_excp_40x(cpu, excp); break; + case POWERPC_EXCP_74xx: + powerpc_excp_74xx(cpu, excp); + break; case POWERPC_EXCP_970: case POWERPC_EXCP_POWER7: case POWERPC_EXCP_POWER8: From 1f6faf8b1451c41add592427c573ca76817aceec Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Fri, 28 Jan 2022 13:15:06 +0100 Subject: [PATCH 065/460] target/ppc: Simplify powerpc_excp_74xx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Differences from the generic powerpc_excp code: - Not BookE, so some MSR bits are cleared at interrupt dispatch; - No MSR_HV; - No power saving states; - No Hypervisor Emulation Assistance; - Not 64 bits; - No System call vectored; - No Alternate Interrupt Location. Exceptions used: POWERPC_EXCP_ALIGN POWERPC_EXCP_DECR POWERPC_EXCP_DSI POWERPC_EXCP_EXTERNAL POWERPC_EXCP_FPU POWERPC_EXCP_IABR POWERPC_EXCP_ISI POWERPC_EXCP_MCHECK POWERPC_EXCP_PERFM POWERPC_EXCP_PROGRAM POWERPC_EXCP_RESET POWERPC_EXCP_SMI POWERPC_EXCP_SYSCALL POWERPC_EXCP_THERM POWERPC_EXCP_TRACE POWERPC_EXCP_VPU POWERPC_EXCP_VPUA Signed-off-by: Fabiano Rosas Message-Id: <20220127201116.1154733-3-farosas@linux.ibm.com> Signed-off-by: Cédric Le Goater --- target/ppc/excp_helper.c | 211 ++------------------------------------- 1 file changed, 9 insertions(+), 202 deletions(-) diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index 0e3d3ffcf4..13e5cb3ddc 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -568,54 +568,26 @@ static void powerpc_excp_74xx(PowerPCCPU *cpu, int excp) excp, env->error_code); /* new srr1 value excluding must-be-zero bits */ - if (excp_model == POWERPC_EXCP_BOOKE) { - msr = env->msr; - } else { - msr = env->msr & ~0x783f0000ULL; - } + msr = env->msr & ~0x783f0000ULL; /* - * new interrupt handler msr preserves existing HV and ME unless + * new interrupt handler msr preserves existing ME unless * explicitly overriden */ - new_msr = env->msr & (((target_ulong)1 << MSR_ME) | MSR_HVB); + new_msr = env->msr & ((target_ulong)1 << MSR_ME); /* target registers */ srr0 = SPR_SRR0; srr1 = SPR_SRR1; - /* - * check for special resume at 0x100 from doze/nap/sleep/winkle on - * P7/P8/P9 - */ - if (env->resume_as_sreset) { - excp = powerpc_reset_wakeup(cs, env, excp, &msr); - } - /* * Hypervisor emulation assistance interrupt only exists on server - * arch 2.05 server or later. We also don't want to generate it if - * we don't have HVB in msr_mask (PAPR mode). + * arch 2.05 server or later. */ - if (excp == POWERPC_EXCP_HV_EMU -#if defined(TARGET_PPC64) - && !(mmu_is_64bit(env->mmu_model) && (env->msr_mask & MSR_HVB)) -#endif /* defined(TARGET_PPC64) */ - - ) { + if (excp == POWERPC_EXCP_HV_EMU) { excp = POWERPC_EXCP_PROGRAM; } -#ifdef TARGET_PPC64 - /* - * SPEU and VPU share the same IVOR but they exist in different - * processors. SPEU is e500v1/2 only and VPU is e6500 only. - */ - if (excp_model == POWERPC_EXCP_BOOKE && excp == POWERPC_EXCP_VPU) { - excp = POWERPC_EXCP_SPEU; - } -#endif - vector = env->excp_vectors[excp]; if (vector == (target_ulong)-1ULL) { cpu_abort(cs, "Raised an exception without defined vector %d\n", @@ -625,22 +597,6 @@ static void powerpc_excp_74xx(PowerPCCPU *cpu, int excp) vector |= env->excp_prefix; switch (excp) { - case POWERPC_EXCP_CRITICAL: /* Critical input */ - switch (excp_model) { - case POWERPC_EXCP_40x: - srr0 = SPR_40x_SRR2; - srr1 = SPR_40x_SRR3; - break; - case POWERPC_EXCP_BOOKE: - srr0 = SPR_BOOKE_CSRR0; - srr1 = SPR_BOOKE_CSRR1; - break; - case POWERPC_EXCP_G2: - break; - default: - goto excp_invalid; - } - break; case POWERPC_EXCP_MCHECK: /* Machine check exception */ if (msr_me == 0) { /* @@ -804,63 +760,9 @@ static void powerpc_excp_74xx(PowerPCCPU *cpu, int excp) new_msr |= (target_ulong)MSR_HVB; } break; - case POWERPC_EXCP_SYSCALL_VECTORED: /* scv exception */ - lev = env->error_code; - dump_syscall(env); - env->nip += 4; - new_msr |= env->msr & ((target_ulong)1 << MSR_EE); - new_msr |= env->msr & ((target_ulong)1 << MSR_RI); - - vector += lev * 0x20; - - env->lr = env->nip; - env->ctr = msr; - break; case POWERPC_EXCP_FPU: /* Floating-point unavailable exception */ - case POWERPC_EXCP_APU: /* Auxiliary processor unavailable */ case POWERPC_EXCP_DECR: /* Decrementer exception */ break; - case POWERPC_EXCP_FIT: /* Fixed-interval timer interrupt */ - /* FIT on 4xx */ - trace_ppc_excp_print("FIT"); - break; - case POWERPC_EXCP_WDT: /* Watchdog timer interrupt */ - trace_ppc_excp_print("WDT"); - switch (excp_model) { - case POWERPC_EXCP_BOOKE: - srr0 = SPR_BOOKE_CSRR0; - srr1 = SPR_BOOKE_CSRR1; - break; - default: - break; - } - break; - case POWERPC_EXCP_DTLB: /* Data TLB error */ - case POWERPC_EXCP_ITLB: /* Instruction TLB error */ - break; - case POWERPC_EXCP_DEBUG: /* Debug interrupt */ - if (env->flags & POWERPC_FLAG_DE) { - /* FIXME: choose one or the other based on CPU type */ - srr0 = SPR_BOOKE_DSRR0; - srr1 = SPR_BOOKE_DSRR1; - - env->spr[SPR_BOOKE_CSRR0] = env->nip; - env->spr[SPR_BOOKE_CSRR1] = msr; - - /* DBSR already modified by caller */ - } else { - cpu_abort(cs, "Debug exception triggered on unsupported model\n"); - } - break; - case POWERPC_EXCP_SPEU: /* SPE/embedded floating-point unavailable/VPU */ - env->spr[SPR_BOOKE_ESR] = ESR_SPV; - break; - case POWERPC_EXCP_DOORI: /* Embedded doorbell interrupt */ - break; - case POWERPC_EXCP_DOORCI: /* Embedded doorbell critical interrupt */ - srr0 = SPR_BOOKE_CSRR0; - srr1 = SPR_BOOKE_CSRR1; - break; case POWERPC_EXCP_RESET: /* System reset exception */ /* A power-saving exception sets ME, otherwise it is unchanged */ if (msr_pow) { @@ -881,92 +783,19 @@ static void powerpc_excp_74xx(PowerPCCPU *cpu, int excp) } } break; - case POWERPC_EXCP_DSEG: /* Data segment exception */ - case POWERPC_EXCP_ISEG: /* Instruction segment exception */ case POWERPC_EXCP_TRACE: /* Trace exception */ break; - case POWERPC_EXCP_HISI: /* Hypervisor instruction storage exception */ - msr |= env->error_code; - /* fall through */ - case POWERPC_EXCP_HDECR: /* Hypervisor decrementer exception */ - case POWERPC_EXCP_HDSI: /* Hypervisor data storage exception */ - case POWERPC_EXCP_HDSEG: /* Hypervisor data segment exception */ - case POWERPC_EXCP_HISEG: /* Hypervisor instruction segment exception */ - case POWERPC_EXCP_SDOOR_HV: /* Hypervisor Doorbell interrupt */ - case POWERPC_EXCP_HV_EMU: - case POWERPC_EXCP_HVIRT: /* Hypervisor virtualization */ - srr0 = SPR_HSRR0; - srr1 = SPR_HSRR1; - new_msr |= (target_ulong)MSR_HVB; - new_msr |= env->msr & ((target_ulong)1 << MSR_RI); - break; case POWERPC_EXCP_VPU: /* Vector unavailable exception */ - case POWERPC_EXCP_VSXU: /* VSX unavailable exception */ - case POWERPC_EXCP_FU: /* Facility unavailable exception */ -#ifdef TARGET_PPC64 - env->spr[SPR_FSCR] |= ((target_ulong)env->error_code << 56); -#endif break; - case POWERPC_EXCP_HV_FU: /* Hypervisor Facility Unavailable Exception */ -#ifdef TARGET_PPC64 - env->spr[SPR_HFSCR] |= ((target_ulong)env->error_code << FSCR_IC_POS); - srr0 = SPR_HSRR0; - srr1 = SPR_HSRR1; - new_msr |= (target_ulong)MSR_HVB; - new_msr |= env->msr & ((target_ulong)1 << MSR_RI); -#endif - break; - case POWERPC_EXCP_PIT: /* Programmable interval timer interrupt */ - trace_ppc_excp_print("PIT"); - break; - case POWERPC_EXCP_IFTLB: /* Instruction fetch TLB error */ - case POWERPC_EXCP_DLTLB: /* Data load TLB miss */ - case POWERPC_EXCP_DSTLB: /* Data store TLB miss */ - switch (excp_model) { - case POWERPC_EXCP_602: - case POWERPC_EXCP_603: - case POWERPC_EXCP_G2: - /* Swap temporary saved registers with GPRs */ - if (!(new_msr & ((target_ulong)1 << MSR_TGPR))) { - new_msr |= (target_ulong)1 << MSR_TGPR; - hreg_swap_gpr_tgpr(env); - } - /* fall through */ - case POWERPC_EXCP_7x5: - ppc_excp_debug_sw_tlb(env, excp); - - msr |= env->crf[0] << 28; - msr |= env->error_code; /* key, D/I, S/L bits */ - /* Set way using a LRU mechanism */ - msr |= ((env->last_way + 1) & (env->nb_ways - 1)) << 17; - break; - default: - cpu_abort(cs, "Invalid TLB miss exception\n"); - break; - } - break; - case POWERPC_EXCP_EFPDI: /* Embedded floating-point data interrupt */ - case POWERPC_EXCP_EFPRI: /* Embedded floating-point round interrupt */ - case POWERPC_EXCP_EPERFM: /* Embedded performance monitor interrupt */ - case POWERPC_EXCP_IO: /* IO error exception */ - case POWERPC_EXCP_RUNM: /* Run mode exception */ - case POWERPC_EXCP_EMUL: /* Emulation trap exception */ - case POWERPC_EXCP_FPA: /* Floating-point assist exception */ - case POWERPC_EXCP_DABR: /* Data address breakpoint */ case POWERPC_EXCP_IABR: /* Instruction address breakpoint */ case POWERPC_EXCP_SMI: /* System management interrupt */ case POWERPC_EXCP_THERM: /* Thermal interrupt */ case POWERPC_EXCP_PERFM: /* Embedded performance monitor interrupt */ case POWERPC_EXCP_VPUA: /* Vector assist exception */ - case POWERPC_EXCP_SOFTP: /* Soft patch exception */ - case POWERPC_EXCP_MAINT: /* Maintenance exception */ - case POWERPC_EXCP_MEXTBR: /* Maskable external breakpoint */ - case POWERPC_EXCP_NMEXTBR: /* Non maskable external breakpoint */ cpu_abort(cs, "%s exception not implemented\n", powerpc_excp_name(excp)); break; default: - excp_invalid: cpu_abort(cs, "Invalid PowerPC exception %d. Aborting\n", excp); break; } @@ -991,33 +820,11 @@ static void powerpc_excp_74xx(PowerPCCPU *cpu, int excp) new_msr |= (target_ulong)1 << MSR_LE; } -#if defined(TARGET_PPC64) - if (excp_model == POWERPC_EXCP_BOOKE) { - if (env->spr[SPR_BOOKE_EPCR] & EPCR_ICM) { - /* Cat.64-bit: EPCR.ICM is copied to MSR.CM */ - new_msr |= (target_ulong)1 << MSR_CM; - } else { - vector = (uint32_t)vector; - } - } else { - if (!msr_isf && !mmu_is_64bit(env->mmu_model)) { - vector = (uint32_t)vector; - } else { - new_msr |= (target_ulong)1 << MSR_SF; - } - } -#endif + /* Save PC */ + env->spr[srr0] = env->nip; - if (excp != POWERPC_EXCP_SYSCALL_VECTORED) { - /* Save PC */ - env->spr[srr0] = env->nip; - - /* Save MSR */ - env->spr[srr1] = msr; - } - - /* This can update new_msr and vector if AIL applies */ - ppc_excp_apply_ail(cpu, excp_model, excp, msr, &new_msr, &vector); + /* Save MSR */ + env->spr[srr1] = msr; powerpc_set_excp_state(cpu, vector, new_msr); } From 3fbb46409f012aea9c1443ae4bd9f49d4fcabd75 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Fri, 28 Jan 2022 13:15:06 +0100 Subject: [PATCH 066/460] target/ppc: 74xx: Machine Check exception cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 74xx don't have an MSR_HV. Also remove 40x and BookE code. Signed-off-by: Fabiano Rosas Message-Id: <20220127201116.1154733-4-farosas@linux.ibm.com> Signed-off-by: Cédric Le Goater --- target/ppc/excp_helper.c | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index 13e5cb3ddc..0d8c66b98f 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -612,34 +612,10 @@ static void powerpc_excp_74xx(PowerPCCPU *cpu, int excp) cs->halted = 1; cpu_interrupt_exittb(cs); } - if (env->msr_mask & MSR_HVB) { - /* - * ISA specifies HV, but can be delivered to guest with HV - * clear (e.g., see FWNMI in PAPR). - */ - new_msr |= (target_ulong)MSR_HVB; - } /* machine check exceptions don't have ME set */ new_msr &= ~((target_ulong)1 << MSR_ME); - /* XXX: should also have something loaded in DAR / DSISR */ - switch (excp_model) { - case POWERPC_EXCP_40x: - srr0 = SPR_40x_SRR2; - srr1 = SPR_40x_SRR3; - break; - case POWERPC_EXCP_BOOKE: - /* FIXME: choose one or the other based on CPU type */ - srr0 = SPR_BOOKE_MCSRR0; - srr1 = SPR_BOOKE_MCSRR1; - - env->spr[SPR_BOOKE_CSRR0] = env->nip; - env->spr[SPR_BOOKE_CSRR1] = msr; - break; - default: - break; - } break; case POWERPC_EXCP_DSI: /* Data storage exception */ trace_ppc_excp_dsi(env->spr[SPR_DSISR], env->spr[SPR_DAR]); From 12e8042698a0682ff87909f513345b6d52c3de2e Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Fri, 28 Jan 2022 13:15:06 +0100 Subject: [PATCH 067/460] target/ppc: 74xx: External interrupt cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 74xx don't have MSR_HV so all the LPES0 logic can be removed. Also remove the BookE IRQ code. Signed-off-by: Fabiano Rosas Message-Id: <20220127201116.1154733-5-farosas@linux.ibm.com> Signed-off-by: Cédric Le Goater --- target/ppc/excp_helper.c | 38 -------------------------------------- 1 file changed, 38 deletions(-) diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index 0d8c66b98f..b9a1d7ae7e 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -555,7 +555,6 @@ static void powerpc_excp_74xx(PowerPCCPU *cpu, int excp) { CPUState *cs = CPU(cpu); CPUPPCState *env = &cpu->env; - int excp_model = env->excp_model; target_ulong msr, new_msr, vector; int srr0, srr1, lev = -1; @@ -625,44 +624,7 @@ static void powerpc_excp_74xx(PowerPCCPU *cpu, int excp) msr |= env->error_code; break; case POWERPC_EXCP_EXTERNAL: /* External input */ - { - bool lpes0; - - cs = CPU(cpu); - - /* - * Exception targeting modifiers - * - * LPES0 is supported on POWER7/8/9 - * LPES1 is not supported (old iSeries mode) - * - * On anything else, we behave as if LPES0 is 1 - * (externals don't alter MSR:HV) - */ -#if defined(TARGET_PPC64) - if (excp_model == POWERPC_EXCP_POWER7 || - excp_model == POWERPC_EXCP_POWER8 || - excp_model == POWERPC_EXCP_POWER9 || - excp_model == POWERPC_EXCP_POWER10) { - lpes0 = !!(env->spr[SPR_LPCR] & LPCR_LPES0); - } else -#endif /* defined(TARGET_PPC64) */ - { - lpes0 = true; - } - - if (!lpes0) { - new_msr |= (target_ulong)MSR_HVB; - new_msr |= env->msr & ((target_ulong)1 << MSR_RI); - srr0 = SPR_HSRR0; - srr1 = SPR_HSRR1; - } - if (env->mpic_proxy) { - /* IACK the IRQ on delivery */ - env->spr[SPR_BOOKE_EPR] = ldl_phys(cs->as, env->mpic_iack); - } break; - } case POWERPC_EXCP_ALIGN: /* Alignment exception */ /* Get rS/rD and rA from faulting opcode */ /* From 0ea2a65fe811700d3be94150b5a83af2ee828c91 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Fri, 28 Jan 2022 13:15:07 +0100 Subject: [PATCH 068/460] target/ppc: 74xx: Program exception cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove the BookE ESR setting. Signed-off-by: Fabiano Rosas Message-Id: <20220127201116.1154733-6-farosas@linux.ibm.com> Signed-off-by: Cédric Le Goater --- target/ppc/excp_helper.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index b9a1d7ae7e..bb17b65dc0 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -650,20 +650,16 @@ static void powerpc_excp_74xx(PowerPCCPU *cpu, int excp) * precise in the MSR. */ msr |= 0x00100000; - env->spr[SPR_BOOKE_ESR] = ESR_FP; break; case POWERPC_EXCP_INVAL: trace_ppc_excp_inval(env->nip); msr |= 0x00080000; - env->spr[SPR_BOOKE_ESR] = ESR_PIL; break; case POWERPC_EXCP_PRIV: msr |= 0x00040000; - env->spr[SPR_BOOKE_ESR] = ESR_PPR; break; case POWERPC_EXCP_TRAP: msr |= 0x00020000; - env->spr[SPR_BOOKE_ESR] = ESR_PTR; break; default: /* Should never occur */ From bca2c6d9e09177c5a6959c3c372dab8211f39404 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Fri, 28 Jan 2022 13:15:07 +0100 Subject: [PATCH 069/460] target/ppc: 74xx: System Call exception cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove the BookE code and add a comment explaining why we need to keep hypercall support even though this CPU does not have a hypervisor mode. Signed-off-by: Fabiano Rosas Message-Id: <20220127201116.1154733-7-farosas@linux.ibm.com> Signed-off-by: Cédric Le Goater --- target/ppc/excp_helper.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index bb17b65dc0..396e25ed35 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -556,7 +556,7 @@ static void powerpc_excp_74xx(PowerPCCPU *cpu, int excp) CPUState *cs = CPU(cpu); CPUPPCState *env = &cpu->env; target_ulong msr, new_msr, vector; - int srr0, srr1, lev = -1; + int srr0, srr1; if (excp <= POWERPC_EXCP_NONE || excp >= POWERPC_EXCP_NB) { cpu_abort(cs, "Invalid PowerPC exception %d. Aborting\n", excp); @@ -669,7 +669,8 @@ static void powerpc_excp_74xx(PowerPCCPU *cpu, int excp) } break; case POWERPC_EXCP_SYSCALL: /* System call exception */ - lev = env->error_code; + { + int lev = env->error_code; if ((lev == 1) && cpu->vhyp) { dump_hcall(env); @@ -683,17 +684,21 @@ static void powerpc_excp_74xx(PowerPCCPU *cpu, int excp) */ env->nip += 4; - /* "PAPR mode" built-in hypercall emulation */ + /* + * The Virtual Open Firmware (VOF) relies on the 'sc 1' + * instruction to communicate with QEMU. The pegasos2 machine + * uses VOF and the 74xx CPUs, so although the 74xx don't have + * 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); return; } - if (lev == 1) { - new_msr |= (target_ulong)MSR_HVB; - } + break; + } case POWERPC_EXCP_FPU: /* Floating-point unavailable exception */ case POWERPC_EXCP_DECR: /* Decrementer exception */ break; From 91a51fecef20ba4ee659a68a55b2b556f070908d Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Fri, 28 Jan 2022 13:15:07 +0100 Subject: [PATCH 070/460] target/ppc: 74xx: System Reset interrupt cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The whole power saving states logic seems to be dependent on HV mode, which don't exist for 74xx so I'm removing it all and leaving the abort message. Signed-off-by: Fabiano Rosas Message-Id: <20220127201116.1154733-8-farosas@linux.ibm.com> Signed-off-by: Cédric Le Goater --- target/ppc/excp_helper.c | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index 396e25ed35..b7921c0956 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -703,23 +703,9 @@ static void powerpc_excp_74xx(PowerPCCPU *cpu, int excp) case POWERPC_EXCP_DECR: /* Decrementer exception */ break; case POWERPC_EXCP_RESET: /* System reset exception */ - /* A power-saving exception sets ME, otherwise it is unchanged */ if (msr_pow) { - /* indicate that we resumed from power save mode */ - msr |= 0x10000; - new_msr |= ((target_ulong)1 << MSR_ME); - } - if (env->msr_mask & MSR_HVB) { - /* - * ISA specifies HV, but can be delivered to guest with HV - * clear (e.g., see FWNMI in PAPR, NMI injection in QEMU). - */ - new_msr |= (target_ulong)MSR_HVB; - } else { - if (msr_pow) { - cpu_abort(cs, "Trying to deliver power-saving system reset " - "exception %d with no HV support\n", excp); - } + cpu_abort(cs, "Trying to deliver power-saving system reset " + "exception %d with no HV support\n", excp); } break; case POWERPC_EXCP_TRACE: /* Trace exception */ From f82db77761806613a62f622db9c1ca613ae1e6ed Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Fri, 28 Jan 2022 13:15:07 +0100 Subject: [PATCH 071/460] target/ppc: 74xx: Set SRRs directly in exception code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 74xx does not have alternate/hypervisor Save and Restore Registers, so we can set SRR0 and SRR1 directly. Signed-off-by: Fabiano Rosas Message-Id: <20220127201116.1154733-9-farosas@linux.ibm.com> Signed-off-by: Cédric Le Goater --- target/ppc/excp_helper.c | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index b7921c0956..4e6bb87b70 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -556,7 +556,6 @@ static void powerpc_excp_74xx(PowerPCCPU *cpu, int excp) CPUState *cs = CPU(cpu); CPUPPCState *env = &cpu->env; target_ulong msr, new_msr, vector; - int srr0, srr1; if (excp <= POWERPC_EXCP_NONE || excp >= POWERPC_EXCP_NB) { cpu_abort(cs, "Invalid PowerPC exception %d. Aborting\n", excp); @@ -575,10 +574,6 @@ static void powerpc_excp_74xx(PowerPCCPU *cpu, int excp) */ new_msr = env->msr & ((target_ulong)1 << MSR_ME); - /* target registers */ - srr0 = SPR_SRR0; - srr1 = SPR_SRR1; - /* * Hypervisor emulation assistance interrupt only exists on server * arch 2.05 server or later. @@ -731,10 +726,6 @@ static void powerpc_excp_74xx(PowerPCCPU *cpu, int excp) cpu_abort(cs, "Trying to deliver HV exception (MSR) %d with " "no HV support\n", excp); } - if (srr0 == SPR_HSRR0) { - cpu_abort(cs, "Trying to deliver HV exception (HSRR) %d with " - "no HV support\n", excp); - } } /* @@ -746,10 +737,10 @@ static void powerpc_excp_74xx(PowerPCCPU *cpu, int excp) } /* Save PC */ - env->spr[srr0] = env->nip; + env->spr[SPR_SRR0] = env->nip; /* Save MSR */ - env->spr[srr1] = msr; + env->spr[SPR_SRR1] = msr; powerpc_set_excp_state(cpu, vector, new_msr); } From fd50a00a57509623ac253614ab10c7ea0eeb8d51 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 20 Jan 2022 12:47:13 +0000 Subject: [PATCH 072/460] Update copyright dates to 2022 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It's a new year; update the copyright strings for our help/version/about information and for our documentation. Signed-off-by: Peter Maydell Reviewed-by: Daniel P. Berrangé Reviewed-by: Philippe Mathieu-Daudé Message-id: 20220120124713.288303-1-peter.maydell@linaro.org --- docs/conf.py | 2 +- include/qemu-common.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index e79015975e..49dab44cca 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -98,7 +98,7 @@ default_role = 'any' # General information about the project. project = u'QEMU' -copyright = u'2021, The QEMU Project Developers' +copyright = u'2022, The QEMU Project Developers' author = u'The QEMU Project Developers' # The version info for the project you're documenting, acts as replacement for diff --git a/include/qemu-common.h b/include/qemu-common.h index 73bcf763ed..0e7e7bab95 100644 --- a/include/qemu-common.h +++ b/include/qemu-common.h @@ -13,7 +13,7 @@ #define TFR(expr) do { if ((expr) != -1) break; } while (errno == EINTR) /* Copyright string for -version arguments, About dialogs, etc */ -#define QEMU_COPYRIGHT "Copyright (c) 2003-2021 " \ +#define QEMU_COPYRIGHT "Copyright (c) 2003-2022 " \ "Fabrice Bellard and the QEMU Project developers" /* Bug reporting information for --help arguments, About dialogs, etc */ From 62a4d87d2e4af378875e0749571fe41cab8706de Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 20 Jan 2022 15:16:09 +0000 Subject: [PATCH 073/460] hw/armv7m: Fix broken VMStateDescription MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In commit d5093d961585f02 we added a VMStateDescription to the TYPE_ARMV7M object, to handle migration of its Clocks. However a cut-and-paste error meant we used the wrong struct name in the VMSTATE_CLOCK() macro arguments. The result was that attempting a 'savevm' might result in an assertion failure. Cc: qemu-stable@nongnu.org Buglink: https://gitlab.com/qemu-project/qemu/-/issues/803 Fixes: d5093d961585f02 Signed-off-by: Peter Maydell Reviewed-by: Ani Sinha Reviewed-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Message-id: 20220120151609.433555-1-peter.maydell@linaro.org --- hw/arm/armv7m.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/arm/armv7m.c b/hw/arm/armv7m.c index 8d08db80be..ceb76df3cd 100644 --- a/hw/arm/armv7m.c +++ b/hw/arm/armv7m.c @@ -520,8 +520,8 @@ static const VMStateDescription vmstate_armv7m = { .version_id = 1, .minimum_version_id = 1, .fields = (VMStateField[]) { - VMSTATE_CLOCK(refclk, SysTickState), - VMSTATE_CLOCK(cpuclk, SysTickState), + VMSTATE_CLOCK(refclk, ARMv7MState), + VMSTATE_CLOCK(cpuclk, ARMv7MState), VMSTATE_END_OF_LIST() } }; From 617dff091f3478db9a44320df03f74b9df0617fb Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 20 Jan 2022 15:16:48 +0000 Subject: [PATCH 074/460] hw/char/exynos4210_uart: Fix crash on trying to load VM state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The exynos4210_uart_post_load() function assumes that it is passed the Exynos4210UartState, but it has been attached to the VMStateDescription for the Exynos4210UartFIFO type. The result is a SIGSEGV when attempting to load VM state for any machine type including this device. Fix the bug by attaching the post-load function to the VMSD for the Exynos4210UartState. This is the logical place for it, because the actions it does relate to the entire UART state, not just the FIFO. Thanks to the bug reporter @TrungNguyen1909 for the clear bug description and the suggested fix. Fixes: c9d3396d80fe7ece9b ("hw/char/exynos4210_uart: Implement post_load function") Resolves: https://gitlab.com/qemu-project/qemu/-/issues/638 Signed-off-by: Peter Maydell Reviewed-by: Guenter Roeck Reviewed-by: Philippe Mathieu-Daudé Message-id: 20220120151648.433736-1-peter.maydell@linaro.org --- hw/char/exynos4210_uart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/char/exynos4210_uart.c b/hw/char/exynos4210_uart.c index 80d401a379..addcd59b02 100644 --- a/hw/char/exynos4210_uart.c +++ b/hw/char/exynos4210_uart.c @@ -628,7 +628,6 @@ static const VMStateDescription vmstate_exynos4210_uart_fifo = { .name = "exynos4210.uart.fifo", .version_id = 1, .minimum_version_id = 1, - .post_load = exynos4210_uart_post_load, .fields = (VMStateField[]) { VMSTATE_UINT32(sp, Exynos4210UartFIFO), VMSTATE_UINT32(rp, Exynos4210UartFIFO), @@ -641,6 +640,7 @@ static const VMStateDescription vmstate_exynos4210_uart = { .name = "exynos4210.uart", .version_id = 1, .minimum_version_id = 1, + .post_load = exynos4210_uart_post_load, .fields = (VMStateField[]) { VMSTATE_STRUCT(rx, Exynos4210UartState, 1, vmstate_exynos4210_uart_fifo, Exynos4210UartFIFO), From 2f93d8b04a1bcc8e0a576e35ae13c96af42634db Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 29 Nov 2021 20:55:05 +0000 Subject: [PATCH 075/460] rtc: Move RTC function prototypes to their own header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit softmmu/rtc.c defines two public functions: qemu_get_timedate() and qemu_timedate_diff(). Currently we keep the prototypes for these in qemu-common.h, but most files don't need them. Move them to their own header, a new include/sysemu/rtc.h. Since the C files using these two functions did not need to include qemu-common.h for any other reason, we can remove those include lines when we add the include of the new rtc.h. The license for the .h file follows that of the softmmu/rtc.c where both the functions are defined. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé --- hw/arm/omap1.c | 2 +- hw/arm/pxa2xx.c | 2 +- hw/arm/strongarm.c | 2 +- hw/misc/mac_via.c | 2 +- hw/misc/macio/cuda.c | 2 +- hw/misc/macio/pmu.c | 2 +- hw/ppc/spapr_rtc.c | 2 +- hw/rtc/allwinner-rtc.c | 2 +- hw/rtc/aspeed_rtc.c | 2 +- hw/rtc/ds1338.c | 2 +- hw/rtc/exynos4210_rtc.c | 2 +- hw/rtc/goldfish_rtc.c | 2 +- hw/rtc/m41t80.c | 2 +- hw/rtc/m48t59.c | 2 +- hw/rtc/mc146818rtc.c | 2 +- hw/rtc/pl031.c | 2 +- hw/rtc/twl92230.c | 2 +- hw/rtc/xlnx-zynqmp-rtc.c | 2 +- hw/s390x/tod-tcg.c | 2 +- hw/scsi/megasas.c | 2 +- include/qemu-common.h | 3 --- include/sysemu/rtc.h | 58 ++++++++++++++++++++++++++++++++++++++++ net/dump.c | 2 +- softmmu/rtc.c | 2 +- 24 files changed, 80 insertions(+), 25 deletions(-) create mode 100644 include/sysemu/rtc.h diff --git a/hw/arm/omap1.c b/hw/arm/omap1.c index 180d3788f8..9852c2a07e 100644 --- a/hw/arm/omap1.c +++ b/hw/arm/omap1.c @@ -21,7 +21,6 @@ #include "qemu/error-report.h" #include "qemu/main-loop.h" #include "qapi/error.h" -#include "qemu-common.h" #include "cpu.h" #include "exec/address-spaces.h" #include "hw/hw.h" @@ -35,6 +34,7 @@ #include "sysemu/qtest.h" #include "sysemu/reset.h" #include "sysemu/runstate.h" +#include "sysemu/rtc.h" #include "qemu/range.h" #include "hw/sysbus.h" #include "qemu/cutils.h" diff --git a/hw/arm/pxa2xx.c b/hw/arm/pxa2xx.c index 15a247efae..a6f938f115 100644 --- a/hw/arm/pxa2xx.c +++ b/hw/arm/pxa2xx.c @@ -8,7 +8,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qemu/error-report.h" #include "qemu/module.h" #include "qapi/error.h" @@ -27,6 +26,7 @@ #include "chardev/char-fe.h" #include "sysemu/blockdev.h" #include "sysemu/qtest.h" +#include "sysemu/rtc.h" #include "qemu/cutils.h" #include "qemu/log.h" #include "qom/object.h" diff --git a/hw/arm/strongarm.c b/hw/arm/strongarm.c index 939a57dda5..39b8f01ac4 100644 --- a/hw/arm/strongarm.c +++ b/hw/arm/strongarm.c @@ -28,7 +28,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "cpu.h" #include "hw/irq.h" #include "hw/qdev-properties.h" @@ -41,6 +40,7 @@ #include "chardev/char-fe.h" #include "chardev/char-serial.h" #include "sysemu/sysemu.h" +#include "sysemu/rtc.h" #include "hw/ssi/ssi.h" #include "qapi/error.h" #include "qemu/cutils.h" diff --git a/hw/misc/mac_via.c b/hw/misc/mac_via.c index b378e6b305..71b74c3372 100644 --- a/hw/misc/mac_via.c +++ b/hw/misc/mac_via.c @@ -16,7 +16,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "migration/vmstate.h" #include "hw/sysbus.h" #include "hw/irq.h" @@ -30,6 +29,7 @@ #include "hw/qdev-properties.h" #include "hw/qdev-properties-system.h" #include "sysemu/block-backend.h" +#include "sysemu/rtc.h" #include "trace.h" #include "qemu/log.h" diff --git a/hw/misc/macio/cuda.c b/hw/misc/macio/cuda.c index e917a6a095..233daf1405 100644 --- a/hw/misc/macio/cuda.c +++ b/hw/misc/macio/cuda.c @@ -24,7 +24,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "hw/ppc/mac.h" #include "hw/qdev-properties.h" #include "migration/vmstate.h" @@ -34,6 +33,7 @@ #include "qapi/error.h" #include "qemu/timer.h" #include "sysemu/runstate.h" +#include "sysemu/rtc.h" #include "qapi/error.h" #include "qemu/cutils.h" #include "qemu/log.h" diff --git a/hw/misc/macio/pmu.c b/hw/misc/macio/pmu.c index eb39c64694..76c608ee19 100644 --- a/hw/misc/macio/pmu.c +++ b/hw/misc/macio/pmu.c @@ -29,7 +29,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "hw/ppc/mac.h" #include "hw/qdev-properties.h" #include "migration/vmstate.h" @@ -41,6 +40,7 @@ #include "qapi/error.h" #include "qemu/timer.h" #include "sysemu/runstate.h" +#include "sysemu/rtc.h" #include "qapi/error.h" #include "qemu/cutils.h" #include "qemu/log.h" diff --git a/hw/ppc/spapr_rtc.c b/hw/ppc/spapr_rtc.c index fba4dfca35..94a5510e4e 100644 --- a/hw/ppc/spapr_rtc.c +++ b/hw/ppc/spapr_rtc.c @@ -26,9 +26,9 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qemu/timer.h" #include "sysemu/sysemu.h" +#include "sysemu/rtc.h" #include "hw/ppc/spapr.h" #include "migration/vmstate.h" #include "qapi/error.h" diff --git a/hw/rtc/allwinner-rtc.c b/hw/rtc/allwinner-rtc.c index 5606a51d5c..7e493f0e79 100644 --- a/hw/rtc/allwinner-rtc.c +++ b/hw/rtc/allwinner-rtc.c @@ -23,9 +23,9 @@ #include "migration/vmstate.h" #include "qemu/log.h" #include "qemu/module.h" -#include "qemu-common.h" #include "hw/qdev-properties.h" #include "hw/rtc/allwinner-rtc.h" +#include "sysemu/rtc.h" #include "trace.h" /* RTC registers */ diff --git a/hw/rtc/aspeed_rtc.c b/hw/rtc/aspeed_rtc.c index 3ca1183558..f6da7b666d 100644 --- a/hw/rtc/aspeed_rtc.c +++ b/hw/rtc/aspeed_rtc.c @@ -7,11 +7,11 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "hw/rtc/aspeed_rtc.h" #include "migration/vmstate.h" #include "qemu/log.h" #include "qemu/timer.h" +#include "sysemu/rtc.h" #include "trace.h" diff --git a/hw/rtc/ds1338.c b/hw/rtc/ds1338.c index bc5ce1a9f4..36d8121ddd 100644 --- a/hw/rtc/ds1338.c +++ b/hw/rtc/ds1338.c @@ -11,12 +11,12 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "hw/i2c/i2c.h" #include "migration/vmstate.h" #include "qemu/bcd.h" #include "qemu/module.h" #include "qom/object.h" +#include "sysemu/rtc.h" /* Size of NVRAM including both the user-accessible area and the * secondary register area. diff --git a/hw/rtc/exynos4210_rtc.c b/hw/rtc/exynos4210_rtc.c index 45c0a951c4..ae67641de6 100644 --- a/hw/rtc/exynos4210_rtc.c +++ b/hw/rtc/exynos4210_rtc.c @@ -26,7 +26,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qemu/log.h" #include "qemu/module.h" #include "hw/sysbus.h" @@ -39,6 +38,7 @@ #include "hw/arm/exynos4210.h" #include "qom/object.h" +#include "sysemu/rtc.h" #define DEBUG_RTC 0 diff --git a/hw/rtc/goldfish_rtc.c b/hw/rtc/goldfish_rtc.c index e07ff0164e..35e493be31 100644 --- a/hw/rtc/goldfish_rtc.c +++ b/hw/rtc/goldfish_rtc.c @@ -20,7 +20,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "hw/rtc/goldfish_rtc.h" #include "migration/vmstate.h" #include "hw/irq.h" @@ -29,6 +28,7 @@ #include "qemu/bitops.h" #include "qemu/timer.h" #include "sysemu/sysemu.h" +#include "sysemu/rtc.h" #include "qemu/cutils.h" #include "qemu/log.h" diff --git a/hw/rtc/m41t80.c b/hw/rtc/m41t80.c index 396d110ba2..a00971a67e 100644 --- a/hw/rtc/m41t80.c +++ b/hw/rtc/m41t80.c @@ -8,13 +8,13 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qemu/log.h" #include "qemu/module.h" #include "qemu/timer.h" #include "qemu/bcd.h" #include "hw/i2c/i2c.h" #include "qom/object.h" +#include "sysemu/rtc.h" #define TYPE_M41T80 "m41t80" OBJECT_DECLARE_SIMPLE_TYPE(M41t80State, M41T80) diff --git a/hw/rtc/m48t59.c b/hw/rtc/m48t59.c index 690f4e071a..74345d9d90 100644 --- a/hw/rtc/m48t59.c +++ b/hw/rtc/m48t59.c @@ -24,12 +24,12 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "hw/irq.h" #include "hw/qdev-properties.h" #include "hw/rtc/m48t59.h" #include "qemu/timer.h" #include "sysemu/runstate.h" +#include "sysemu/rtc.h" #include "sysemu/sysemu.h" #include "hw/sysbus.h" #include "qapi/error.h" diff --git a/hw/rtc/mc146818rtc.c b/hw/rtc/mc146818rtc.c index 4fbafddb22..e61a0cced4 100644 --- a/hw/rtc/mc146818rtc.c +++ b/hw/rtc/mc146818rtc.c @@ -23,7 +23,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qemu/cutils.h" #include "qemu/module.h" #include "qemu/bcd.h" @@ -36,6 +35,7 @@ #include "sysemu/replay.h" #include "sysemu/reset.h" #include "sysemu/runstate.h" +#include "sysemu/rtc.h" #include "hw/rtc/mc146818rtc.h" #include "hw/rtc/mc146818rtc_regs.h" #include "migration/vmstate.h" diff --git a/hw/rtc/pl031.c b/hw/rtc/pl031.c index e7ced90b02..38d9d3c2f3 100644 --- a/hw/rtc/pl031.c +++ b/hw/rtc/pl031.c @@ -12,7 +12,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "hw/rtc/pl031.h" #include "migration/vmstate.h" #include "hw/irq.h" @@ -20,6 +19,7 @@ #include "hw/sysbus.h" #include "qemu/timer.h" #include "sysemu/sysemu.h" +#include "sysemu/rtc.h" #include "qemu/cutils.h" #include "qemu/log.h" #include "qemu/module.h" diff --git a/hw/rtc/twl92230.c b/hw/rtc/twl92230.c index 0922df5ad3..e8d5eda3fc 100644 --- a/hw/rtc/twl92230.c +++ b/hw/rtc/twl92230.c @@ -20,13 +20,13 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qemu/timer.h" #include "hw/i2c/i2c.h" #include "hw/irq.h" #include "migration/qemu-file-types.h" #include "migration/vmstate.h" #include "sysemu/sysemu.h" +#include "sysemu/rtc.h" #include "qemu/bcd.h" #include "qemu/module.h" #include "qom/object.h" diff --git a/hw/rtc/xlnx-zynqmp-rtc.c b/hw/rtc/xlnx-zynqmp-rtc.c index 2bcd14d779..3e7d61a41c 100644 --- a/hw/rtc/xlnx-zynqmp-rtc.c +++ b/hw/rtc/xlnx-zynqmp-rtc.c @@ -25,7 +25,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "hw/sysbus.h" #include "hw/register.h" #include "qemu/bitops.h" @@ -34,6 +33,7 @@ #include "hw/irq.h" #include "qemu/cutils.h" #include "sysemu/sysemu.h" +#include "sysemu/rtc.h" #include "trace.h" #include "hw/rtc/xlnx-zynqmp-rtc.h" #include "migration/vmstate.h" diff --git a/hw/s390x/tod-tcg.c b/hw/s390x/tod-tcg.c index 9bb94ff72b..7646b4aa38 100644 --- a/hw/s390x/tod-tcg.c +++ b/hw/s390x/tod-tcg.c @@ -9,7 +9,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qapi/error.h" #include "hw/s390x/tod.h" #include "qemu/timer.h" @@ -17,6 +16,7 @@ #include "qemu/module.h" #include "cpu.h" #include "tcg/tcg_s390x.h" +#include "sysemu/rtc.h" static void qemu_s390_tod_get(const S390TODState *td, S390TOD *tod, Error **errp) diff --git a/hw/scsi/megasas.c b/hw/scsi/megasas.c index c9da5ce0b5..f638ff8831 100644 --- a/hw/scsi/megasas.c +++ b/hw/scsi/megasas.c @@ -19,11 +19,11 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "hw/pci/pci.h" #include "hw/qdev-properties.h" #include "sysemu/dma.h" #include "sysemu/block-backend.h" +#include "sysemu/rtc.h" #include "hw/pci/msi.h" #include "hw/pci/msix.h" #include "qemu/iov.h" diff --git a/include/qemu-common.h b/include/qemu-common.h index 0e7e7bab95..68b2e3bc10 100644 --- a/include/qemu-common.h +++ b/include/qemu-common.h @@ -26,9 +26,6 @@ int qemu_main(int argc, char **argv, char **envp); #endif -void qemu_get_timedate(struct tm *tm, int offset); -int qemu_timedate_diff(struct tm *tm); - void *qemu_oom_check(void *ptr); ssize_t qemu_write_full(int fd, const void *buf, size_t count) diff --git a/include/sysemu/rtc.h b/include/sysemu/rtc.h new file mode 100644 index 0000000000..159702b45b --- /dev/null +++ b/include/sysemu/rtc.h @@ -0,0 +1,58 @@ +/* + * RTC configuration and clock read + * + * Copyright (c) 2003-2021 QEMU contributors + * + * 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 SYSEMU_RTC_H +#define SYSEMU_RTC_H + +/** + * qemu_get_timedate: Get the current RTC time + * @tm: struct tm to fill in with RTC time + * @offset: offset in seconds to adjust the RTC time by before + * converting to struct tm format. + * + * This function fills in @tm with the current RTC time, as adjusted + * by @offset (for example, if @offset is 3600 then the returned time/date + * will be one hour further ahead than the current RTC time). + * + * The usual use is by RTC device models, which should call this function + * to find the time/date value that they should return to the guest + * when it reads the RTC registers. + * + * The behaviour of the clock whose value this function returns will + * depend on the -rtc command line option passed by the user. + */ +void qemu_get_timedate(struct tm *tm, int offset); + +/** + * qemu_timedate_diff: Return difference between a struct tm and the RTC + * @tm: struct tm containing the date/time to compare against + * + * Returns the difference in seconds between the RTC clock time + * and the date/time specified in @tm. For example, if @tm specifies + * a timestamp one hour further ahead than the current RTC time + * then this function will return 3600. + */ +int qemu_timedate_diff(struct tm *tm); + +#endif diff --git a/net/dump.c b/net/dump.c index a07ba62401..6a63b15359 100644 --- a/net/dump.c +++ b/net/dump.c @@ -23,7 +23,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "clients.h" #include "qapi/error.h" #include "qemu/error-report.h" @@ -33,6 +32,7 @@ #include "qapi/visitor.h" #include "net/filter.h" #include "qom/object.h" +#include "sysemu/rtc.h" typedef struct DumpState { int64_t start_ts; diff --git a/softmmu/rtc.c b/softmmu/rtc.c index 5632684fc9..7e2956f81e 100644 --- a/softmmu/rtc.c +++ b/softmmu/rtc.c @@ -23,7 +23,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qemu/cutils.h" #include "qapi/error.h" #include "qapi/qmp/qerror.h" @@ -33,6 +32,7 @@ #include "qom/object.h" #include "sysemu/replay.h" #include "sysemu/sysemu.h" +#include "sysemu/rtc.h" static enum { RTC_BASE_UTC, From 8c1c0a1b72f1d9138c2a219064072a4462d4cc8e Mon Sep 17 00:00:00 2001 From: Francisco Iglesias Date: Fri, 21 Jan 2022 16:11:32 +0000 Subject: [PATCH 076/460] hw/misc: Add a model of Versal's PMC SLCR Add a model of Versal's PMC SLCR (system-level control registers). Signed-off-by: Francisco Iglesias Signed-off-by: Edgar E. Iglesias Reviewed-by: Peter Maydell Reviewed-by: Luc Michel Message-id: 20220121161141.14389-2-francisco.iglesias@xilinx.com Signed-off-by: Peter Maydell --- hw/misc/meson.build | 5 +- hw/misc/xlnx-versal-pmc-iou-slcr.c | 1446 ++++++++++++++++++++ include/hw/misc/xlnx-versal-pmc-iou-slcr.h | 78 ++ 3 files changed, 1528 insertions(+), 1 deletion(-) create mode 100644 hw/misc/xlnx-versal-pmc-iou-slcr.c create mode 100644 include/hw/misc/xlnx-versal-pmc-iou-slcr.h diff --git a/hw/misc/meson.build b/hw/misc/meson.build index d1a1169108..6dcbe044f3 100644 --- a/hw/misc/meson.build +++ b/hw/misc/meson.build @@ -84,7 +84,10 @@ softmmu_ss.add(when: 'CONFIG_RASPI', if_true: files( )) softmmu_ss.add(when: 'CONFIG_SLAVIO', if_true: files('slavio_misc.c')) softmmu_ss.add(when: 'CONFIG_ZYNQ', if_true: files('zynq_slcr.c')) -softmmu_ss.add(when: 'CONFIG_XLNX_VERSAL', if_true: files('xlnx-versal-xramc.c')) +softmmu_ss.add(when: 'CONFIG_XLNX_VERSAL', if_true: files( + 'xlnx-versal-xramc.c', + 'xlnx-versal-pmc-iou-slcr.c', +)) softmmu_ss.add(when: 'CONFIG_STM32F2XX_SYSCFG', if_true: files('stm32f2xx_syscfg.c')) softmmu_ss.add(when: 'CONFIG_STM32F4XX_SYSCFG', if_true: files('stm32f4xx_syscfg.c')) softmmu_ss.add(when: 'CONFIG_STM32F4XX_EXTI', if_true: files('stm32f4xx_exti.c')) diff --git a/hw/misc/xlnx-versal-pmc-iou-slcr.c b/hw/misc/xlnx-versal-pmc-iou-slcr.c new file mode 100644 index 0000000000..07b7ebc217 --- /dev/null +++ b/hw/misc/xlnx-versal-pmc-iou-slcr.c @@ -0,0 +1,1446 @@ +/* + * QEMU model of Versal's PMC IOU SLCR (system level control registers) + * + * Copyright (c) 2021 Xilinx Inc. + * Written by 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 "hw/sysbus.h" +#include "hw/register.h" +#include "hw/irq.h" +#include "qemu/bitops.h" +#include "qemu/log.h" +#include "migration/vmstate.h" +#include "hw/qdev-properties.h" +#include "hw/misc/xlnx-versal-pmc-iou-slcr.h" + +#ifndef XILINX_VERSAL_PMC_IOU_SLCR_ERR_DEBUG +#define XILINX_VERSAL_PMC_IOU_SLCR_ERR_DEBUG 0 +#endif + +REG32(MIO_PIN_0, 0x0) + FIELD(MIO_PIN_0, L3_SEL, 7, 3) + FIELD(MIO_PIN_0, L2_SEL, 5, 2) + FIELD(MIO_PIN_0, L1_SEL, 3, 2) + FIELD(MIO_PIN_0, L0_SEL, 1, 2) +REG32(MIO_PIN_1, 0x4) + FIELD(MIO_PIN_1, L3_SEL, 7, 3) + FIELD(MIO_PIN_1, L2_SEL, 5, 2) + FIELD(MIO_PIN_1, L1_SEL, 3, 2) + FIELD(MIO_PIN_1, L0_SEL, 1, 2) +REG32(MIO_PIN_2, 0x8) + FIELD(MIO_PIN_2, L3_SEL, 7, 3) + FIELD(MIO_PIN_2, L2_SEL, 5, 2) + FIELD(MIO_PIN_2, L1_SEL, 3, 2) + FIELD(MIO_PIN_2, L0_SEL, 1, 2) +REG32(MIO_PIN_3, 0xc) + FIELD(MIO_PIN_3, L3_SEL, 7, 3) + FIELD(MIO_PIN_3, L2_SEL, 5, 2) + FIELD(MIO_PIN_3, L1_SEL, 3, 2) + FIELD(MIO_PIN_3, L0_SEL, 1, 2) +REG32(MIO_PIN_4, 0x10) + FIELD(MIO_PIN_4, L3_SEL, 7, 3) + FIELD(MIO_PIN_4, L2_SEL, 5, 2) + FIELD(MIO_PIN_4, L1_SEL, 3, 2) + FIELD(MIO_PIN_4, L0_SEL, 1, 2) +REG32(MIO_PIN_5, 0x14) + FIELD(MIO_PIN_5, L3_SEL, 7, 3) + FIELD(MIO_PIN_5, L2_SEL, 5, 2) + FIELD(MIO_PIN_5, L1_SEL, 3, 2) + FIELD(MIO_PIN_5, L0_SEL, 1, 2) +REG32(MIO_PIN_6, 0x18) + FIELD(MIO_PIN_6, L3_SEL, 7, 3) + FIELD(MIO_PIN_6, L2_SEL, 5, 2) + FIELD(MIO_PIN_6, L1_SEL, 3, 2) + FIELD(MIO_PIN_6, L0_SEL, 1, 2) +REG32(MIO_PIN_7, 0x1c) + FIELD(MIO_PIN_7, L3_SEL, 7, 3) + FIELD(MIO_PIN_7, L2_SEL, 5, 2) + FIELD(MIO_PIN_7, L1_SEL, 3, 2) + FIELD(MIO_PIN_7, L0_SEL, 1, 2) +REG32(MIO_PIN_8, 0x20) + FIELD(MIO_PIN_8, L3_SEL, 7, 3) + FIELD(MIO_PIN_8, L2_SEL, 5, 2) + FIELD(MIO_PIN_8, L1_SEL, 3, 2) + FIELD(MIO_PIN_8, L0_SEL, 1, 2) +REG32(MIO_PIN_9, 0x24) + FIELD(MIO_PIN_9, L3_SEL, 7, 3) + FIELD(MIO_PIN_9, L2_SEL, 5, 2) + FIELD(MIO_PIN_9, L1_SEL, 3, 2) + FIELD(MIO_PIN_9, L0_SEL, 1, 2) +REG32(MIO_PIN_10, 0x28) + FIELD(MIO_PIN_10, L3_SEL, 7, 3) + FIELD(MIO_PIN_10, L2_SEL, 5, 2) + FIELD(MIO_PIN_10, L1_SEL, 3, 2) + FIELD(MIO_PIN_10, L0_SEL, 1, 2) +REG32(MIO_PIN_11, 0x2c) + FIELD(MIO_PIN_11, L3_SEL, 7, 3) + FIELD(MIO_PIN_11, L2_SEL, 5, 2) + FIELD(MIO_PIN_11, L1_SEL, 3, 2) + FIELD(MIO_PIN_11, L0_SEL, 1, 2) +REG32(MIO_PIN_12, 0x30) + FIELD(MIO_PIN_12, L3_SEL, 7, 3) + FIELD(MIO_PIN_12, L2_SEL, 5, 2) + FIELD(MIO_PIN_12, L1_SEL, 3, 2) + FIELD(MIO_PIN_12, L0_SEL, 1, 2) +REG32(MIO_PIN_13, 0x34) + FIELD(MIO_PIN_13, L3_SEL, 7, 3) + FIELD(MIO_PIN_13, L2_SEL, 5, 2) + FIELD(MIO_PIN_13, L1_SEL, 3, 2) + FIELD(MIO_PIN_13, L0_SEL, 1, 2) +REG32(MIO_PIN_14, 0x38) + FIELD(MIO_PIN_14, L3_SEL, 7, 3) + FIELD(MIO_PIN_14, L2_SEL, 5, 2) + FIELD(MIO_PIN_14, L1_SEL, 3, 2) + FIELD(MIO_PIN_14, L0_SEL, 1, 2) +REG32(MIO_PIN_15, 0x3c) + FIELD(MIO_PIN_15, L3_SEL, 7, 3) + FIELD(MIO_PIN_15, L2_SEL, 5, 2) + FIELD(MIO_PIN_15, L1_SEL, 3, 2) + FIELD(MIO_PIN_15, L0_SEL, 1, 2) +REG32(MIO_PIN_16, 0x40) + FIELD(MIO_PIN_16, L3_SEL, 7, 3) + FIELD(MIO_PIN_16, L2_SEL, 5, 2) + FIELD(MIO_PIN_16, L1_SEL, 3, 2) + FIELD(MIO_PIN_16, L0_SEL, 1, 2) +REG32(MIO_PIN_17, 0x44) + FIELD(MIO_PIN_17, L3_SEL, 7, 3) + FIELD(MIO_PIN_17, L2_SEL, 5, 2) + FIELD(MIO_PIN_17, L1_SEL, 3, 2) + FIELD(MIO_PIN_17, L0_SEL, 1, 2) +REG32(MIO_PIN_18, 0x48) + FIELD(MIO_PIN_18, L3_SEL, 7, 3) + FIELD(MIO_PIN_18, L2_SEL, 5, 2) + FIELD(MIO_PIN_18, L1_SEL, 3, 2) + FIELD(MIO_PIN_18, L0_SEL, 1, 2) +REG32(MIO_PIN_19, 0x4c) + FIELD(MIO_PIN_19, L3_SEL, 7, 3) + FIELD(MIO_PIN_19, L2_SEL, 5, 2) + FIELD(MIO_PIN_19, L1_SEL, 3, 2) + FIELD(MIO_PIN_19, L0_SEL, 1, 2) +REG32(MIO_PIN_20, 0x50) + FIELD(MIO_PIN_20, L3_SEL, 7, 3) + FIELD(MIO_PIN_20, L2_SEL, 5, 2) + FIELD(MIO_PIN_20, L1_SEL, 3, 2) + FIELD(MIO_PIN_20, L0_SEL, 1, 2) +REG32(MIO_PIN_21, 0x54) + FIELD(MIO_PIN_21, L3_SEL, 7, 3) + FIELD(MIO_PIN_21, L2_SEL, 5, 2) + FIELD(MIO_PIN_21, L1_SEL, 3, 2) + FIELD(MIO_PIN_21, L0_SEL, 1, 2) +REG32(MIO_PIN_22, 0x58) + FIELD(MIO_PIN_22, L3_SEL, 7, 3) + FIELD(MIO_PIN_22, L2_SEL, 5, 2) + FIELD(MIO_PIN_22, L1_SEL, 3, 2) + FIELD(MIO_PIN_22, L0_SEL, 1, 2) +REG32(MIO_PIN_23, 0x5c) + FIELD(MIO_PIN_23, L3_SEL, 7, 3) + FIELD(MIO_PIN_23, L2_SEL, 5, 2) + FIELD(MIO_PIN_23, L1_SEL, 3, 2) + FIELD(MIO_PIN_23, L0_SEL, 1, 2) +REG32(MIO_PIN_24, 0x60) + FIELD(MIO_PIN_24, L3_SEL, 7, 3) + FIELD(MIO_PIN_24, L2_SEL, 5, 2) + FIELD(MIO_PIN_24, L1_SEL, 3, 2) + FIELD(MIO_PIN_24, L0_SEL, 1, 2) +REG32(MIO_PIN_25, 0x64) + FIELD(MIO_PIN_25, L3_SEL, 7, 3) + FIELD(MIO_PIN_25, L2_SEL, 5, 2) + FIELD(MIO_PIN_25, L1_SEL, 3, 2) + FIELD(MIO_PIN_25, L0_SEL, 1, 2) +REG32(MIO_PIN_26, 0x68) + FIELD(MIO_PIN_26, L3_SEL, 7, 3) + FIELD(MIO_PIN_26, L2_SEL, 5, 2) + FIELD(MIO_PIN_26, L1_SEL, 3, 2) + FIELD(MIO_PIN_26, L0_SEL, 1, 2) +REG32(MIO_PIN_27, 0x6c) + FIELD(MIO_PIN_27, L3_SEL, 7, 3) + FIELD(MIO_PIN_27, L2_SEL, 5, 2) + FIELD(MIO_PIN_27, L1_SEL, 3, 2) + FIELD(MIO_PIN_27, L0_SEL, 1, 2) +REG32(MIO_PIN_28, 0x70) + FIELD(MIO_PIN_28, L3_SEL, 7, 3) + FIELD(MIO_PIN_28, L2_SEL, 5, 2) + FIELD(MIO_PIN_28, L1_SEL, 3, 2) + FIELD(MIO_PIN_28, L0_SEL, 1, 2) +REG32(MIO_PIN_29, 0x74) + FIELD(MIO_PIN_29, L3_SEL, 7, 3) + FIELD(MIO_PIN_29, L2_SEL, 5, 2) + FIELD(MIO_PIN_29, L1_SEL, 3, 2) + FIELD(MIO_PIN_29, L0_SEL, 1, 2) +REG32(MIO_PIN_30, 0x78) + FIELD(MIO_PIN_30, L3_SEL, 7, 3) + FIELD(MIO_PIN_30, L2_SEL, 5, 2) + FIELD(MIO_PIN_30, L1_SEL, 3, 2) + FIELD(MIO_PIN_30, L0_SEL, 1, 2) +REG32(MIO_PIN_31, 0x7c) + FIELD(MIO_PIN_31, L3_SEL, 7, 3) + FIELD(MIO_PIN_31, L2_SEL, 5, 2) + FIELD(MIO_PIN_31, L1_SEL, 3, 2) + FIELD(MIO_PIN_31, L0_SEL, 1, 2) +REG32(MIO_PIN_32, 0x80) + FIELD(MIO_PIN_32, L3_SEL, 7, 3) + FIELD(MIO_PIN_32, L2_SEL, 5, 2) + FIELD(MIO_PIN_32, L1_SEL, 3, 2) + FIELD(MIO_PIN_32, L0_SEL, 1, 2) +REG32(MIO_PIN_33, 0x84) + FIELD(MIO_PIN_33, L3_SEL, 7, 3) + FIELD(MIO_PIN_33, L2_SEL, 5, 2) + FIELD(MIO_PIN_33, L1_SEL, 3, 2) + FIELD(MIO_PIN_33, L0_SEL, 1, 2) +REG32(MIO_PIN_34, 0x88) + FIELD(MIO_PIN_34, L3_SEL, 7, 3) + FIELD(MIO_PIN_34, L2_SEL, 5, 2) + FIELD(MIO_PIN_34, L1_SEL, 3, 2) + FIELD(MIO_PIN_34, L0_SEL, 1, 2) +REG32(MIO_PIN_35, 0x8c) + FIELD(MIO_PIN_35, L3_SEL, 7, 3) + FIELD(MIO_PIN_35, L2_SEL, 5, 2) + FIELD(MIO_PIN_35, L1_SEL, 3, 2) + FIELD(MIO_PIN_35, L0_SEL, 1, 2) +REG32(MIO_PIN_36, 0x90) + FIELD(MIO_PIN_36, L3_SEL, 7, 3) + FIELD(MIO_PIN_36, L2_SEL, 5, 2) + FIELD(MIO_PIN_36, L1_SEL, 3, 2) + FIELD(MIO_PIN_36, L0_SEL, 1, 2) +REG32(MIO_PIN_37, 0x94) + FIELD(MIO_PIN_37, L3_SEL, 7, 3) + FIELD(MIO_PIN_37, L2_SEL, 5, 2) + FIELD(MIO_PIN_37, L1_SEL, 3, 2) + FIELD(MIO_PIN_37, L0_SEL, 1, 2) +REG32(MIO_PIN_38, 0x98) + FIELD(MIO_PIN_38, L3_SEL, 7, 3) + FIELD(MIO_PIN_38, L2_SEL, 5, 2) + FIELD(MIO_PIN_38, L1_SEL, 3, 2) + FIELD(MIO_PIN_38, L0_SEL, 1, 2) +REG32(MIO_PIN_39, 0x9c) + FIELD(MIO_PIN_39, L3_SEL, 7, 3) + FIELD(MIO_PIN_39, L2_SEL, 5, 2) + FIELD(MIO_PIN_39, L1_SEL, 3, 2) + FIELD(MIO_PIN_39, L0_SEL, 1, 2) +REG32(MIO_PIN_40, 0xa0) + FIELD(MIO_PIN_40, L3_SEL, 7, 3) + FIELD(MIO_PIN_40, L2_SEL, 5, 2) + FIELD(MIO_PIN_40, L1_SEL, 3, 2) + FIELD(MIO_PIN_40, L0_SEL, 1, 2) +REG32(MIO_PIN_41, 0xa4) + FIELD(MIO_PIN_41, L3_SEL, 7, 3) + FIELD(MIO_PIN_41, L2_SEL, 5, 2) + FIELD(MIO_PIN_41, L1_SEL, 3, 2) + FIELD(MIO_PIN_41, L0_SEL, 1, 2) +REG32(MIO_PIN_42, 0xa8) + FIELD(MIO_PIN_42, L3_SEL, 7, 3) + FIELD(MIO_PIN_42, L2_SEL, 5, 2) + FIELD(MIO_PIN_42, L1_SEL, 3, 2) + FIELD(MIO_PIN_42, L0_SEL, 1, 2) +REG32(MIO_PIN_43, 0xac) + FIELD(MIO_PIN_43, L3_SEL, 7, 3) + FIELD(MIO_PIN_43, L2_SEL, 5, 2) + FIELD(MIO_PIN_43, L1_SEL, 3, 2) + FIELD(MIO_PIN_43, L0_SEL, 1, 2) +REG32(MIO_PIN_44, 0xb0) + FIELD(MIO_PIN_44, L3_SEL, 7, 3) + FIELD(MIO_PIN_44, L2_SEL, 5, 2) + FIELD(MIO_PIN_44, L1_SEL, 3, 2) + FIELD(MIO_PIN_44, L0_SEL, 1, 2) +REG32(MIO_PIN_45, 0xb4) + FIELD(MIO_PIN_45, L3_SEL, 7, 3) + FIELD(MIO_PIN_45, L2_SEL, 5, 2) + FIELD(MIO_PIN_45, L1_SEL, 3, 2) + FIELD(MIO_PIN_45, L0_SEL, 1, 2) +REG32(MIO_PIN_46, 0xb8) + FIELD(MIO_PIN_46, L3_SEL, 7, 3) + FIELD(MIO_PIN_46, L2_SEL, 5, 2) + FIELD(MIO_PIN_46, L1_SEL, 3, 2) + FIELD(MIO_PIN_46, L0_SEL, 1, 2) +REG32(MIO_PIN_47, 0xbc) + FIELD(MIO_PIN_47, L3_SEL, 7, 3) + FIELD(MIO_PIN_47, L2_SEL, 5, 2) + FIELD(MIO_PIN_47, L1_SEL, 3, 2) + FIELD(MIO_PIN_47, L0_SEL, 1, 2) +REG32(MIO_PIN_48, 0xc0) + FIELD(MIO_PIN_48, L3_SEL, 7, 3) + FIELD(MIO_PIN_48, L2_SEL, 5, 2) + FIELD(MIO_PIN_48, L1_SEL, 3, 2) + FIELD(MIO_PIN_48, L0_SEL, 1, 2) +REG32(MIO_PIN_49, 0xc4) + FIELD(MIO_PIN_49, L3_SEL, 7, 3) + FIELD(MIO_PIN_49, L2_SEL, 5, 2) + FIELD(MIO_PIN_49, L1_SEL, 3, 2) + FIELD(MIO_PIN_49, L0_SEL, 1, 2) +REG32(MIO_PIN_50, 0xc8) + FIELD(MIO_PIN_50, L3_SEL, 7, 3) + FIELD(MIO_PIN_50, L2_SEL, 5, 2) + FIELD(MIO_PIN_50, L1_SEL, 3, 2) + FIELD(MIO_PIN_50, L0_SEL, 1, 2) +REG32(MIO_PIN_51, 0xcc) + FIELD(MIO_PIN_51, L3_SEL, 7, 3) + FIELD(MIO_PIN_51, L2_SEL, 5, 2) + FIELD(MIO_PIN_51, L1_SEL, 3, 2) + FIELD(MIO_PIN_51, L0_SEL, 1, 2) +REG32(BNK0_EN_RX, 0x100) + FIELD(BNK0_EN_RX, BNK0_EN_RX, 0, 26) +REG32(BNK0_SEL_RX0, 0x104) +REG32(BNK0_SEL_RX1, 0x108) + FIELD(BNK0_SEL_RX1, BNK0_SEL_RX, 0, 20) +REG32(BNK0_EN_RX_SCHMITT_HYST, 0x10c) + FIELD(BNK0_EN_RX_SCHMITT_HYST, BNK0_EN_RX_SCHMITT_HYST, 0, 26) +REG32(BNK0_EN_WK_PD, 0x110) + FIELD(BNK0_EN_WK_PD, BNK0_EN_WK_PD, 0, 26) +REG32(BNK0_EN_WK_PU, 0x114) + FIELD(BNK0_EN_WK_PU, BNK0_EN_WK_PU, 0, 26) +REG32(BNK0_SEL_DRV0, 0x118) +REG32(BNK0_SEL_DRV1, 0x11c) + FIELD(BNK0_SEL_DRV1, BNK0_SEL_DRV, 0, 20) +REG32(BNK0_SEL_SLEW, 0x120) + FIELD(BNK0_SEL_SLEW, BNK0_SEL_SLEW, 0, 26) +REG32(BNK0_EN_DFT_OPT_INV, 0x124) + FIELD(BNK0_EN_DFT_OPT_INV, BNK0_EN_DFT_OPT_INV, 0, 26) +REG32(BNK0_EN_PAD2PAD_LOOPBACK, 0x128) + FIELD(BNK0_EN_PAD2PAD_LOOPBACK, BNK0_EN_PAD2PAD_LOOPBACK, 0, 13) +REG32(BNK0_RX_SPARE0, 0x12c) +REG32(BNK0_RX_SPARE1, 0x130) + FIELD(BNK0_RX_SPARE1, BNK0_RX_SPARE, 0, 20) +REG32(BNK0_TX_SPARE0, 0x134) +REG32(BNK0_TX_SPARE1, 0x138) + FIELD(BNK0_TX_SPARE1, BNK0_TX_SPARE, 0, 20) +REG32(BNK0_SEL_EN1P8, 0x13c) + FIELD(BNK0_SEL_EN1P8, BNK0_SEL_EN1P8, 0, 1) +REG32(BNK0_EN_B_POR_DETECT, 0x140) + FIELD(BNK0_EN_B_POR_DETECT, BNK0_EN_B_POR_DETECT, 0, 1) +REG32(BNK0_LPF_BYP_POR_DETECT, 0x144) + FIELD(BNK0_LPF_BYP_POR_DETECT, BNK0_LPF_BYP_POR_DETECT, 0, 1) +REG32(BNK0_EN_LATCH, 0x148) + FIELD(BNK0_EN_LATCH, BNK0_EN_LATCH, 0, 1) +REG32(BNK0_VBG_LPF_BYP_B, 0x14c) + FIELD(BNK0_VBG_LPF_BYP_B, BNK0_VBG_LPF_BYP_B, 0, 1) +REG32(BNK0_EN_AMP_B, 0x150) + FIELD(BNK0_EN_AMP_B, BNK0_EN_AMP_B, 0, 2) +REG32(BNK0_SPARE_BIAS, 0x154) + FIELD(BNK0_SPARE_BIAS, BNK0_SPARE_BIAS, 0, 4) +REG32(BNK0_DRIVER_BIAS, 0x158) + FIELD(BNK0_DRIVER_BIAS, BNK0_DRIVER_BIAS, 0, 15) +REG32(BNK0_VMODE, 0x15c) + FIELD(BNK0_VMODE, BNK0_VMODE, 0, 1) +REG32(BNK0_SEL_AUX_IO_RX, 0x160) + FIELD(BNK0_SEL_AUX_IO_RX, BNK0_SEL_AUX_IO_RX, 0, 26) +REG32(BNK0_EN_TX_HS_MODE, 0x164) + FIELD(BNK0_EN_TX_HS_MODE, BNK0_EN_TX_HS_MODE, 0, 26) +REG32(MIO_MST_TRI0, 0x200) + FIELD(MIO_MST_TRI0, PIN_25_TRI, 25, 1) + FIELD(MIO_MST_TRI0, PIN_24_TRI, 24, 1) + FIELD(MIO_MST_TRI0, PIN_23_TRI, 23, 1) + FIELD(MIO_MST_TRI0, PIN_22_TRI, 22, 1) + FIELD(MIO_MST_TRI0, PIN_21_TRI, 21, 1) + FIELD(MIO_MST_TRI0, PIN_20_TRI, 20, 1) + FIELD(MIO_MST_TRI0, PIN_19_TRI, 19, 1) + FIELD(MIO_MST_TRI0, PIN_18_TRI, 18, 1) + FIELD(MIO_MST_TRI0, PIN_17_TRI, 17, 1) + FIELD(MIO_MST_TRI0, PIN_16_TRI, 16, 1) + FIELD(MIO_MST_TRI0, PIN_15_TRI, 15, 1) + FIELD(MIO_MST_TRI0, PIN_14_TRI, 14, 1) + FIELD(MIO_MST_TRI0, PIN_13_TRI, 13, 1) + FIELD(MIO_MST_TRI0, PIN_12_TRI, 12, 1) + FIELD(MIO_MST_TRI0, PIN_11_TRI, 11, 1) + FIELD(MIO_MST_TRI0, PIN_10_TRI, 10, 1) + FIELD(MIO_MST_TRI0, PIN_09_TRI, 9, 1) + FIELD(MIO_MST_TRI0, PIN_08_TRI, 8, 1) + FIELD(MIO_MST_TRI0, PIN_07_TRI, 7, 1) + FIELD(MIO_MST_TRI0, PIN_06_TRI, 6, 1) + FIELD(MIO_MST_TRI0, PIN_05_TRI, 5, 1) + FIELD(MIO_MST_TRI0, PIN_04_TRI, 4, 1) + FIELD(MIO_MST_TRI0, PIN_03_TRI, 3, 1) + FIELD(MIO_MST_TRI0, PIN_02_TRI, 2, 1) + FIELD(MIO_MST_TRI0, PIN_01_TRI, 1, 1) + FIELD(MIO_MST_TRI0, PIN_00_TRI, 0, 1) +REG32(MIO_MST_TRI1, 0x204) + FIELD(MIO_MST_TRI1, PIN_51_TRI, 25, 1) + FIELD(MIO_MST_TRI1, PIN_50_TRI, 24, 1) + FIELD(MIO_MST_TRI1, PIN_49_TRI, 23, 1) + FIELD(MIO_MST_TRI1, PIN_48_TRI, 22, 1) + FIELD(MIO_MST_TRI1, PIN_47_TRI, 21, 1) + FIELD(MIO_MST_TRI1, PIN_46_TRI, 20, 1) + FIELD(MIO_MST_TRI1, PIN_45_TRI, 19, 1) + FIELD(MIO_MST_TRI1, PIN_44_TRI, 18, 1) + FIELD(MIO_MST_TRI1, PIN_43_TRI, 17, 1) + FIELD(MIO_MST_TRI1, PIN_42_TRI, 16, 1) + FIELD(MIO_MST_TRI1, PIN_41_TRI, 15, 1) + FIELD(MIO_MST_TRI1, PIN_40_TRI, 14, 1) + FIELD(MIO_MST_TRI1, PIN_39_TRI, 13, 1) + FIELD(MIO_MST_TRI1, PIN_38_TRI, 12, 1) + FIELD(MIO_MST_TRI1, PIN_37_TRI, 11, 1) + FIELD(MIO_MST_TRI1, PIN_36_TRI, 10, 1) + FIELD(MIO_MST_TRI1, PIN_35_TRI, 9, 1) + FIELD(MIO_MST_TRI1, PIN_34_TRI, 8, 1) + FIELD(MIO_MST_TRI1, PIN_33_TRI, 7, 1) + FIELD(MIO_MST_TRI1, PIN_32_TRI, 6, 1) + FIELD(MIO_MST_TRI1, PIN_31_TRI, 5, 1) + FIELD(MIO_MST_TRI1, PIN_30_TRI, 4, 1) + FIELD(MIO_MST_TRI1, PIN_29_TRI, 3, 1) + FIELD(MIO_MST_TRI1, PIN_28_TRI, 2, 1) + FIELD(MIO_MST_TRI1, PIN_27_TRI, 1, 1) + FIELD(MIO_MST_TRI1, PIN_26_TRI, 0, 1) +REG32(BNK1_EN_RX, 0x300) + FIELD(BNK1_EN_RX, BNK1_EN_RX, 0, 26) +REG32(BNK1_SEL_RX0, 0x304) +REG32(BNK1_SEL_RX1, 0x308) + FIELD(BNK1_SEL_RX1, BNK1_SEL_RX, 0, 20) +REG32(BNK1_EN_RX_SCHMITT_HYST, 0x30c) + FIELD(BNK1_EN_RX_SCHMITT_HYST, BNK1_EN_RX_SCHMITT_HYST, 0, 26) +REG32(BNK1_EN_WK_PD, 0x310) + FIELD(BNK1_EN_WK_PD, BNK1_EN_WK_PD, 0, 26) +REG32(BNK1_EN_WK_PU, 0x314) + FIELD(BNK1_EN_WK_PU, BNK1_EN_WK_PU, 0, 26) +REG32(BNK1_SEL_DRV0, 0x318) +REG32(BNK1_SEL_DRV1, 0x31c) + FIELD(BNK1_SEL_DRV1, BNK1_SEL_DRV, 0, 20) +REG32(BNK1_SEL_SLEW, 0x320) + FIELD(BNK1_SEL_SLEW, BNK1_SEL_SLEW, 0, 26) +REG32(BNK1_EN_DFT_OPT_INV, 0x324) + FIELD(BNK1_EN_DFT_OPT_INV, BNK1_EN_DFT_OPT_INV, 0, 26) +REG32(BNK1_EN_PAD2PAD_LOOPBACK, 0x328) + FIELD(BNK1_EN_PAD2PAD_LOOPBACK, BNK1_EN_PAD2PAD_LOOPBACK, 0, 13) +REG32(BNK1_RX_SPARE0, 0x32c) +REG32(BNK1_RX_SPARE1, 0x330) + FIELD(BNK1_RX_SPARE1, BNK1_RX_SPARE, 0, 20) +REG32(BNK1_TX_SPARE0, 0x334) +REG32(BNK1_TX_SPARE1, 0x338) + FIELD(BNK1_TX_SPARE1, BNK1_TX_SPARE, 0, 20) +REG32(BNK1_SEL_EN1P8, 0x33c) + FIELD(BNK1_SEL_EN1P8, BNK1_SEL_EN1P8, 0, 1) +REG32(BNK1_EN_B_POR_DETECT, 0x340) + FIELD(BNK1_EN_B_POR_DETECT, BNK1_EN_B_POR_DETECT, 0, 1) +REG32(BNK1_LPF_BYP_POR_DETECT, 0x344) + FIELD(BNK1_LPF_BYP_POR_DETECT, BNK1_LPF_BYP_POR_DETECT, 0, 1) +REG32(BNK1_EN_LATCH, 0x348) + FIELD(BNK1_EN_LATCH, BNK1_EN_LATCH, 0, 1) +REG32(BNK1_VBG_LPF_BYP_B, 0x34c) + FIELD(BNK1_VBG_LPF_BYP_B, BNK1_VBG_LPF_BYP_B, 0, 1) +REG32(BNK1_EN_AMP_B, 0x350) + FIELD(BNK1_EN_AMP_B, BNK1_EN_AMP_B, 0, 2) +REG32(BNK1_SPARE_BIAS, 0x354) + FIELD(BNK1_SPARE_BIAS, BNK1_SPARE_BIAS, 0, 4) +REG32(BNK1_DRIVER_BIAS, 0x358) + FIELD(BNK1_DRIVER_BIAS, BNK1_DRIVER_BIAS, 0, 15) +REG32(BNK1_VMODE, 0x35c) + FIELD(BNK1_VMODE, BNK1_VMODE, 0, 1) +REG32(BNK1_SEL_AUX_IO_RX, 0x360) + FIELD(BNK1_SEL_AUX_IO_RX, BNK1_SEL_AUX_IO_RX, 0, 26) +REG32(BNK1_EN_TX_HS_MODE, 0x364) + FIELD(BNK1_EN_TX_HS_MODE, BNK1_EN_TX_HS_MODE, 0, 26) +REG32(SD0_CLK_CTRL, 0x400) + FIELD(SD0_CLK_CTRL, SDIO0_FBCLK_SEL, 2, 1) + FIELD(SD0_CLK_CTRL, SDIO0_RX_SRC_SEL, 0, 2) +REG32(SD0_CTRL_REG, 0x404) + FIELD(SD0_CTRL_REG, SD0_EMMC_SEL, 0, 1) +REG32(SD0_CONFIG_REG1, 0x410) + FIELD(SD0_CONFIG_REG1, SD0_BASECLK, 7, 8) + FIELD(SD0_CONFIG_REG1, SD0_TUNIGCOUNT, 1, 6) + FIELD(SD0_CONFIG_REG1, SD0_ASYNCWKPENA, 0, 1) +REG32(SD0_CONFIG_REG2, 0x414) + FIELD(SD0_CONFIG_REG2, SD0_SLOTTYPE, 12, 2) + FIELD(SD0_CONFIG_REG2, SD0_ASYCINTR, 11, 1) + FIELD(SD0_CONFIG_REG2, SD0_64BIT, 10, 1) + FIELD(SD0_CONFIG_REG2, SD0_1P8V, 9, 1) + FIELD(SD0_CONFIG_REG2, SD0_3P0V, 8, 1) + FIELD(SD0_CONFIG_REG2, SD0_3P3V, 7, 1) + FIELD(SD0_CONFIG_REG2, SD0_SUSPRES, 6, 1) + FIELD(SD0_CONFIG_REG2, SD0_SDMA, 5, 1) + FIELD(SD0_CONFIG_REG2, SD0_HIGHSPEED, 4, 1) + FIELD(SD0_CONFIG_REG2, SD0_ADMA2, 3, 1) + FIELD(SD0_CONFIG_REG2, SD0_8BIT, 2, 1) + FIELD(SD0_CONFIG_REG2, SD0_MAXBLK, 0, 2) +REG32(SD0_CONFIG_REG3, 0x418) + FIELD(SD0_CONFIG_REG3, SD0_TUNINGSDR50, 10, 1) + FIELD(SD0_CONFIG_REG3, SD0_RETUNETMR, 6, 4) + FIELD(SD0_CONFIG_REG3, SD0_DDRIVER, 5, 1) + FIELD(SD0_CONFIG_REG3, SD0_CDRIVER, 4, 1) + FIELD(SD0_CONFIG_REG3, SD0_ADRIVER, 3, 1) + FIELD(SD0_CONFIG_REG3, SD0_DDR50, 2, 1) + FIELD(SD0_CONFIG_REG3, SD0_SDR104, 1, 1) + FIELD(SD0_CONFIG_REG3, SD0_SDR50, 0, 1) +REG32(SD0_INITPRESET, 0x41c) + FIELD(SD0_INITPRESET, SD0_INITPRESET, 0, 13) +REG32(SD0_DSPPRESET, 0x420) + FIELD(SD0_DSPPRESET, SD0_DSPPRESET, 0, 13) +REG32(SD0_HSPDPRESET, 0x424) + FIELD(SD0_HSPDPRESET, SD0_HSPDPRESET, 0, 13) +REG32(SD0_SDR12PRESET, 0x428) + FIELD(SD0_SDR12PRESET, SD0_SDR12PRESET, 0, 13) +REG32(SD0_SDR25PRESET, 0x42c) + FIELD(SD0_SDR25PRESET, SD0_SDR25PRESET, 0, 13) +REG32(SD0_SDR50PRSET, 0x430) + FIELD(SD0_SDR50PRSET, SD0_SDR50PRESET, 0, 13) +REG32(SD0_SDR104PRST, 0x434) + FIELD(SD0_SDR104PRST, SD0_SDR104PRESET, 0, 13) +REG32(SD0_DDR50PRESET, 0x438) + FIELD(SD0_DDR50PRESET, SD0_DDR50PRESET, 0, 13) +REG32(SD0_MAXCUR1P8, 0x43c) + FIELD(SD0_MAXCUR1P8, SD0_MAXCUR1P8, 0, 8) +REG32(SD0_MAXCUR3P0, 0x440) + FIELD(SD0_MAXCUR3P0, SD0_MAXCUR3P0, 0, 8) +REG32(SD0_MAXCUR3P3, 0x444) + FIELD(SD0_MAXCUR3P3, SD0_MAXCUR3P3, 0, 8) +REG32(SD0_DLL_CTRL, 0x448) + FIELD(SD0_DLL_CTRL, SD0_CLKSTABLE_CFG, 9, 1) + FIELD(SD0_DLL_CTRL, SD0_DLL_CFG, 5, 4) + FIELD(SD0_DLL_CTRL, SD0_DLL_PSDONE, 4, 1) + FIELD(SD0_DLL_CTRL, SD0_DLL_OVF, 3, 1) + FIELD(SD0_DLL_CTRL, SD0_DLL_RST, 2, 1) + FIELD(SD0_DLL_CTRL, SD0_DLL_TESTMODE, 1, 1) + FIELD(SD0_DLL_CTRL, SD0_DLL_LOCK, 0, 1) +REG32(SD0_CDN_CTRL, 0x44c) + FIELD(SD0_CDN_CTRL, SD0_CDN_CTRL, 0, 1) +REG32(SD0_DLL_TEST, 0x450) + FIELD(SD0_DLL_TEST, DLL_DIV, 16, 8) + FIELD(SD0_DLL_TEST, DLL_TX_SEL, 9, 7) + FIELD(SD0_DLL_TEST, DLL_RX_SEL, 0, 9) +REG32(SD0_RX_TUNING_SEL, 0x454) + FIELD(SD0_RX_TUNING_SEL, SD0_RX_SEL, 0, 9) +REG32(SD0_DLL_DIV_MAP0, 0x458) + FIELD(SD0_DLL_DIV_MAP0, DIV_3, 24, 8) + FIELD(SD0_DLL_DIV_MAP0, DIV_2, 16, 8) + FIELD(SD0_DLL_DIV_MAP0, DIV_1, 8, 8) + FIELD(SD0_DLL_DIV_MAP0, DIV_0, 0, 8) +REG32(SD0_DLL_DIV_MAP1, 0x45c) + FIELD(SD0_DLL_DIV_MAP1, DIV_7, 24, 8) + FIELD(SD0_DLL_DIV_MAP1, DIV_6, 16, 8) + FIELD(SD0_DLL_DIV_MAP1, DIV_5, 8, 8) + FIELD(SD0_DLL_DIV_MAP1, DIV_4, 0, 8) +REG32(SD0_IOU_COHERENT_CTRL, 0x460) + FIELD(SD0_IOU_COHERENT_CTRL, SD0_AXI_COH, 0, 4) +REG32(SD0_IOU_INTERCONNECT_ROUTE, 0x464) + FIELD(SD0_IOU_INTERCONNECT_ROUTE, SD0, 0, 1) +REG32(SD0_IOU_RAM, 0x468) + FIELD(SD0_IOU_RAM, EMASA0, 6, 1) + FIELD(SD0_IOU_RAM, EMAB0, 3, 3) + FIELD(SD0_IOU_RAM, EMAA0, 0, 3) +REG32(SD0_IOU_INTERCONNECT_QOS, 0x46c) + FIELD(SD0_IOU_INTERCONNECT_QOS, SD0_QOS, 0, 4) +REG32(SD1_CLK_CTRL, 0x480) + FIELD(SD1_CLK_CTRL, SDIO1_FBCLK_SEL, 1, 1) + FIELD(SD1_CLK_CTRL, SDIO1_RX_SRC_SEL, 0, 1) +REG32(SD1_CTRL_REG, 0x484) + FIELD(SD1_CTRL_REG, SD1_EMMC_SEL, 0, 1) +REG32(SD1_CONFIG_REG1, 0x490) + FIELD(SD1_CONFIG_REG1, SD1_BASECLK, 7, 8) + FIELD(SD1_CONFIG_REG1, SD1_TUNIGCOUNT, 1, 6) + FIELD(SD1_CONFIG_REG1, SD1_ASYNCWKPENA, 0, 1) +REG32(SD1_CONFIG_REG2, 0x494) + FIELD(SD1_CONFIG_REG2, SD1_SLOTTYPE, 12, 2) + FIELD(SD1_CONFIG_REG2, SD1_ASYCINTR, 11, 1) + FIELD(SD1_CONFIG_REG2, SD1_64BIT, 10, 1) + FIELD(SD1_CONFIG_REG2, SD1_1P8V, 9, 1) + FIELD(SD1_CONFIG_REG2, SD1_3P0V, 8, 1) + FIELD(SD1_CONFIG_REG2, SD1_3P3V, 7, 1) + FIELD(SD1_CONFIG_REG2, SD1_SUSPRES, 6, 1) + FIELD(SD1_CONFIG_REG2, SD1_SDMA, 5, 1) + FIELD(SD1_CONFIG_REG2, SD1_HIGHSPEED, 4, 1) + FIELD(SD1_CONFIG_REG2, SD1_ADMA2, 3, 1) + FIELD(SD1_CONFIG_REG2, SD1_8BIT, 2, 1) + FIELD(SD1_CONFIG_REG2, SD1_MAXBLK, 0, 2) +REG32(SD1_CONFIG_REG3, 0x498) + FIELD(SD1_CONFIG_REG3, SD1_TUNINGSDR50, 10, 1) + FIELD(SD1_CONFIG_REG3, SD1_RETUNETMR, 6, 4) + FIELD(SD1_CONFIG_REG3, SD1_DDRIVER, 5, 1) + FIELD(SD1_CONFIG_REG3, SD1_CDRIVER, 4, 1) + FIELD(SD1_CONFIG_REG3, SD1_ADRIVER, 3, 1) + FIELD(SD1_CONFIG_REG3, SD1_DDR50, 2, 1) + FIELD(SD1_CONFIG_REG3, SD1_SDR104, 1, 1) + FIELD(SD1_CONFIG_REG3, SD1_SDR50, 0, 1) +REG32(SD1_INITPRESET, 0x49c) + FIELD(SD1_INITPRESET, SD1_INITPRESET, 0, 13) +REG32(SD1_DSPPRESET, 0x4a0) + FIELD(SD1_DSPPRESET, SD1_DSPPRESET, 0, 13) +REG32(SD1_HSPDPRESET, 0x4a4) + FIELD(SD1_HSPDPRESET, SD1_HSPDPRESET, 0, 13) +REG32(SD1_SDR12PRESET, 0x4a8) + FIELD(SD1_SDR12PRESET, SD1_SDR12PRESET, 0, 13) +REG32(SD1_SDR25PRESET, 0x4ac) + FIELD(SD1_SDR25PRESET, SD1_SDR25PRESET, 0, 13) +REG32(SD1_SDR50PRSET, 0x4b0) + FIELD(SD1_SDR50PRSET, SD1_SDR50PRESET, 0, 13) +REG32(SD1_SDR104PRST, 0x4b4) + FIELD(SD1_SDR104PRST, SD1_SDR104PRESET, 0, 13) +REG32(SD1_DDR50PRESET, 0x4b8) + FIELD(SD1_DDR50PRESET, SD1_DDR50PRESET, 0, 13) +REG32(SD1_MAXCUR1P8, 0x4bc) + FIELD(SD1_MAXCUR1P8, SD1_MAXCUR1P8, 0, 8) +REG32(SD1_MAXCUR3P0, 0x4c0) + FIELD(SD1_MAXCUR3P0, SD1_MAXCUR3P0, 0, 8) +REG32(SD1_MAXCUR3P3, 0x4c4) + FIELD(SD1_MAXCUR3P3, SD1_MAXCUR3P3, 0, 8) +REG32(SD1_DLL_CTRL, 0x4c8) + FIELD(SD1_DLL_CTRL, SD1_CLKSTABLE_CFG, 9, 1) + FIELD(SD1_DLL_CTRL, SD1_DLL_CFG, 5, 4) + FIELD(SD1_DLL_CTRL, SD1_DLL_PSDONE, 4, 1) + FIELD(SD1_DLL_CTRL, SD1_DLL_OVF, 3, 1) + FIELD(SD1_DLL_CTRL, SD1_DLL_RST, 2, 1) + FIELD(SD1_DLL_CTRL, SD1_DLL_TESTMODE, 1, 1) + FIELD(SD1_DLL_CTRL, SD1_DLL_LOCK, 0, 1) +REG32(SD1_CDN_CTRL, 0x4cc) + FIELD(SD1_CDN_CTRL, SD1_CDN_CTRL, 0, 1) +REG32(SD1_DLL_TEST, 0x4d0) + FIELD(SD1_DLL_TEST, DLL_DIV, 16, 8) + FIELD(SD1_DLL_TEST, DLL_TX_SEL, 9, 7) + FIELD(SD1_DLL_TEST, DLL_RX_SEL, 0, 9) +REG32(SD1_RX_TUNING_SEL, 0x4d4) + FIELD(SD1_RX_TUNING_SEL, SD1_RX_SEL, 0, 9) +REG32(SD1_DLL_DIV_MAP0, 0x4d8) + FIELD(SD1_DLL_DIV_MAP0, DIV_3, 24, 8) + FIELD(SD1_DLL_DIV_MAP0, DIV_2, 16, 8) + FIELD(SD1_DLL_DIV_MAP0, DIV_1, 8, 8) + FIELD(SD1_DLL_DIV_MAP0, DIV_0, 0, 8) +REG32(SD1_DLL_DIV_MAP1, 0x4dc) + FIELD(SD1_DLL_DIV_MAP1, DIV_7, 24, 8) + FIELD(SD1_DLL_DIV_MAP1, DIV_6, 16, 8) + FIELD(SD1_DLL_DIV_MAP1, DIV_5, 8, 8) + FIELD(SD1_DLL_DIV_MAP1, DIV_4, 0, 8) +REG32(SD1_IOU_COHERENT_CTRL, 0x4e0) + FIELD(SD1_IOU_COHERENT_CTRL, SD1_AXI_COH, 0, 4) +REG32(SD1_IOU_INTERCONNECT_ROUTE, 0x4e4) + FIELD(SD1_IOU_INTERCONNECT_ROUTE, SD1, 0, 1) +REG32(SD1_IOU_RAM, 0x4e8) + FIELD(SD1_IOU_RAM, EMASA0, 6, 1) + FIELD(SD1_IOU_RAM, EMAB0, 3, 3) + FIELD(SD1_IOU_RAM, EMAA0, 0, 3) +REG32(SD1_IOU_INTERCONNECT_QOS, 0x4ec) + FIELD(SD1_IOU_INTERCONNECT_QOS, SD1_QOS, 0, 4) +REG32(OSPI_QSPI_IOU_AXI_MUX_SEL, 0x504) + FIELD(OSPI_QSPI_IOU_AXI_MUX_SEL, OSPI_MUX_SEL, 1, 1) + FIELD(OSPI_QSPI_IOU_AXI_MUX_SEL, QSPI_OSPI_MUX_SEL, 0, 1) +REG32(QSPI_IOU_COHERENT_CTRL, 0x508) + FIELD(QSPI_IOU_COHERENT_CTRL, QSPI_AXI_COH, 0, 4) +REG32(QSPI_IOU_INTERCONNECT_ROUTE, 0x50c) + FIELD(QSPI_IOU_INTERCONNECT_ROUTE, QSPI, 0, 1) +REG32(QSPI_IOU_RAM, 0x510) + FIELD(QSPI_IOU_RAM, EMASA1, 13, 1) + FIELD(QSPI_IOU_RAM, EMAB1, 10, 3) + FIELD(QSPI_IOU_RAM, EMAA1, 7, 3) + FIELD(QSPI_IOU_RAM, EMASA0, 6, 1) + FIELD(QSPI_IOU_RAM, EMAB0, 3, 3) + FIELD(QSPI_IOU_RAM, EMAA0, 0, 3) +REG32(QSPI_IOU_INTERCONNECT_QOS, 0x514) + FIELD(QSPI_IOU_INTERCONNECT_QOS, QSPI_QOS, 0, 4) +REG32(OSPI_IOU_COHERENT_CTRL, 0x530) + FIELD(OSPI_IOU_COHERENT_CTRL, OSPI_AXI_COH, 0, 4) +REG32(OSPI_IOU_INTERCONNECT_ROUTE, 0x534) + FIELD(OSPI_IOU_INTERCONNECT_ROUTE, OSPI, 0, 1) +REG32(OSPI_IOU_RAM, 0x538) + FIELD(OSPI_IOU_RAM, EMAS0, 5, 1) + FIELD(OSPI_IOU_RAM, EMAW0, 3, 2) + FIELD(OSPI_IOU_RAM, EMA0, 0, 3) +REG32(OSPI_IOU_INTERCONNECT_QOS, 0x53c) + FIELD(OSPI_IOU_INTERCONNECT_QOS, OSPI_QOS, 0, 4) +REG32(OSPI_REFCLK_DLY_CTRL, 0x540) + FIELD(OSPI_REFCLK_DLY_CTRL, DLY1, 3, 2) + FIELD(OSPI_REFCLK_DLY_CTRL, DLY0, 0, 3) +REG32(CUR_PWR_ST, 0x600) + FIELD(CUR_PWR_ST, U2PMU, 0, 2) +REG32(CONNECT_ST, 0x604) + FIELD(CONNECT_ST, U2PMU, 0, 1) +REG32(PW_STATE_REQ, 0x608) + FIELD(PW_STATE_REQ, BIT_1_0, 0, 2) +REG32(HOST_U2_PORT_DISABLE, 0x60c) + FIELD(HOST_U2_PORT_DISABLE, BIT_0, 0, 1) +REG32(DBG_U2PMU, 0x610) +REG32(DBG_U2PMU_EXT1, 0x614) +REG32(DBG_U2PMU_EXT2, 0x618) + FIELD(DBG_U2PMU_EXT2, BIT_67_64, 0, 4) +REG32(PME_GEN_U2PMU, 0x61c) + FIELD(PME_GEN_U2PMU, BIT_0, 0, 1) +REG32(PWR_CONFIG_USB2, 0x620) + FIELD(PWR_CONFIG_USB2, STRAP, 0, 30) +REG32(PHY_HUB, 0x624) + FIELD(PHY_HUB, VBUS_CTRL, 1, 1) + FIELD(PHY_HUB, OVER_CURRENT, 0, 1) +REG32(CTRL, 0x700) + FIELD(CTRL, SLVERR_ENABLE, 0, 1) +REG32(ISR, 0x800) + FIELD(ISR, ADDR_DECODE_ERR, 0, 1) +REG32(IMR, 0x804) + FIELD(IMR, ADDR_DECODE_ERR, 0, 1) +REG32(IER, 0x808) + FIELD(IER, ADDR_DECODE_ERR, 0, 1) +REG32(IDR, 0x80c) + FIELD(IDR, ADDR_DECODE_ERR, 0, 1) +REG32(ITR, 0x810) + FIELD(ITR, ADDR_DECODE_ERR, 0, 1) +REG32(PARITY_ISR, 0x814) + FIELD(PARITY_ISR, PERR_AXI_SD1_IOU, 12, 1) + FIELD(PARITY_ISR, PERR_AXI_SD0_IOU, 11, 1) + FIELD(PARITY_ISR, PERR_AXI_QSPI_IOU, 10, 1) + FIELD(PARITY_ISR, PERR_AXI_OSPI_IOU, 9, 1) + FIELD(PARITY_ISR, PERR_IOU_SD1, 8, 1) + FIELD(PARITY_ISR, PERR_IOU_SD0, 7, 1) + FIELD(PARITY_ISR, PERR_IOU_QSPI1, 6, 1) + FIELD(PARITY_ISR, PERR_IOUSLCR_SECURE_APB, 5, 1) + FIELD(PARITY_ISR, PERR_IOUSLCR_APB, 4, 1) + FIELD(PARITY_ISR, PERR_QSPI0_APB, 3, 1) + FIELD(PARITY_ISR, PERR_OSPI_APB, 2, 1) + FIELD(PARITY_ISR, PERR_I2C_APB, 1, 1) + FIELD(PARITY_ISR, PERR_GPIO_APB, 0, 1) +REG32(PARITY_IMR, 0x818) + FIELD(PARITY_IMR, PERR_AXI_SD1_IOU, 12, 1) + FIELD(PARITY_IMR, PERR_AXI_SD0_IOU, 11, 1) + FIELD(PARITY_IMR, PERR_AXI_QSPI_IOU, 10, 1) + FIELD(PARITY_IMR, PERR_AXI_OSPI_IOU, 9, 1) + FIELD(PARITY_IMR, PERR_IOU_SD1, 8, 1) + FIELD(PARITY_IMR, PERR_IOU_SD0, 7, 1) + FIELD(PARITY_IMR, PERR_IOU_QSPI1, 6, 1) + FIELD(PARITY_IMR, PERR_IOUSLCR_SECURE_APB, 5, 1) + FIELD(PARITY_IMR, PERR_IOUSLCR_APB, 4, 1) + FIELD(PARITY_IMR, PERR_QSPI0_APB, 3, 1) + FIELD(PARITY_IMR, PERR_OSPI_APB, 2, 1) + FIELD(PARITY_IMR, PERR_I2C_APB, 1, 1) + FIELD(PARITY_IMR, PERR_GPIO_APB, 0, 1) +REG32(PARITY_IER, 0x81c) + FIELD(PARITY_IER, PERR_AXI_SD1_IOU, 12, 1) + FIELD(PARITY_IER, PERR_AXI_SD0_IOU, 11, 1) + FIELD(PARITY_IER, PERR_AXI_QSPI_IOU, 10, 1) + FIELD(PARITY_IER, PERR_AXI_OSPI_IOU, 9, 1) + FIELD(PARITY_IER, PERR_IOU_SD1, 8, 1) + FIELD(PARITY_IER, PERR_IOU_SD0, 7, 1) + FIELD(PARITY_IER, PERR_IOU_QSPI1, 6, 1) + FIELD(PARITY_IER, PERR_IOUSLCR_SECURE_APB, 5, 1) + FIELD(PARITY_IER, PERR_IOUSLCR_APB, 4, 1) + FIELD(PARITY_IER, PERR_QSPI0_APB, 3, 1) + FIELD(PARITY_IER, PERR_OSPI_APB, 2, 1) + FIELD(PARITY_IER, PERR_I2C_APB, 1, 1) + FIELD(PARITY_IER, PERR_GPIO_APB, 0, 1) +REG32(PARITY_IDR, 0x820) + FIELD(PARITY_IDR, PERR_AXI_SD1_IOU, 12, 1) + FIELD(PARITY_IDR, PERR_AXI_SD0_IOU, 11, 1) + FIELD(PARITY_IDR, PERR_AXI_QSPI_IOU, 10, 1) + FIELD(PARITY_IDR, PERR_AXI_OSPI_IOU, 9, 1) + FIELD(PARITY_IDR, PERR_IOU_SD1, 8, 1) + FIELD(PARITY_IDR, PERR_IOU_SD0, 7, 1) + FIELD(PARITY_IDR, PERR_IOU_QSPI1, 6, 1) + FIELD(PARITY_IDR, PERR_IOUSLCR_SECURE_APB, 5, 1) + FIELD(PARITY_IDR, PERR_IOUSLCR_APB, 4, 1) + FIELD(PARITY_IDR, PERR_QSPI0_APB, 3, 1) + FIELD(PARITY_IDR, PERR_OSPI_APB, 2, 1) + FIELD(PARITY_IDR, PERR_I2C_APB, 1, 1) + FIELD(PARITY_IDR, PERR_GPIO_APB, 0, 1) +REG32(PARITY_ITR, 0x824) + FIELD(PARITY_ITR, PERR_AXI_SD1_IOU, 12, 1) + FIELD(PARITY_ITR, PERR_AXI_SD0_IOU, 11, 1) + FIELD(PARITY_ITR, PERR_AXI_QSPI_IOU, 10, 1) + FIELD(PARITY_ITR, PERR_AXI_OSPI_IOU, 9, 1) + FIELD(PARITY_ITR, PERR_IOU_SD1, 8, 1) + FIELD(PARITY_ITR, PERR_IOU_SD0, 7, 1) + FIELD(PARITY_ITR, PERR_IOU_QSPI1, 6, 1) + FIELD(PARITY_ITR, PERR_IOUSLCR_SECURE_APB, 5, 1) + FIELD(PARITY_ITR, PERR_IOUSLCR_APB, 4, 1) + FIELD(PARITY_ITR, PERR_QSPI0_APB, 3, 1) + FIELD(PARITY_ITR, PERR_OSPI_APB, 2, 1) + FIELD(PARITY_ITR, PERR_I2C_APB, 1, 1) + FIELD(PARITY_ITR, PERR_GPIO_APB, 0, 1) +REG32(WPROT0, 0x828) + FIELD(WPROT0, ACTIVE, 0, 1) + +static void parity_imr_update_irq(XlnxVersalPmcIouSlcr *s) +{ + bool pending = s->regs[R_PARITY_ISR] & ~s->regs[R_PARITY_IMR]; + qemu_set_irq(s->irq_parity_imr, pending); +} + +static void parity_isr_postw(RegisterInfo *reg, uint64_t val64) +{ + XlnxVersalPmcIouSlcr *s = XILINX_VERSAL_PMC_IOU_SLCR(reg->opaque); + parity_imr_update_irq(s); +} + +static uint64_t parity_ier_prew(RegisterInfo *reg, uint64_t val64) +{ + XlnxVersalPmcIouSlcr *s = XILINX_VERSAL_PMC_IOU_SLCR(reg->opaque); + uint32_t val = val64; + + s->regs[R_PARITY_IMR] &= ~val; + parity_imr_update_irq(s); + return 0; +} + +static uint64_t parity_idr_prew(RegisterInfo *reg, uint64_t val64) +{ + XlnxVersalPmcIouSlcr *s = XILINX_VERSAL_PMC_IOU_SLCR(reg->opaque); + uint32_t val = val64; + + s->regs[R_PARITY_IMR] |= val; + parity_imr_update_irq(s); + return 0; +} + +static uint64_t parity_itr_prew(RegisterInfo *reg, uint64_t val64) +{ + XlnxVersalPmcIouSlcr *s = XILINX_VERSAL_PMC_IOU_SLCR(reg->opaque); + uint32_t val = val64; + + s->regs[R_PARITY_ISR] |= val; + parity_imr_update_irq(s); + return 0; +} + +static void imr_update_irq(XlnxVersalPmcIouSlcr *s) +{ + bool pending = s->regs[R_ISR] & ~s->regs[R_IMR]; + qemu_set_irq(s->irq_imr, pending); +} + +static void isr_postw(RegisterInfo *reg, uint64_t val64) +{ + XlnxVersalPmcIouSlcr *s = XILINX_VERSAL_PMC_IOU_SLCR(reg->opaque); + imr_update_irq(s); +} + +static uint64_t ier_prew(RegisterInfo *reg, uint64_t val64) +{ + XlnxVersalPmcIouSlcr *s = XILINX_VERSAL_PMC_IOU_SLCR(reg->opaque); + uint32_t val = val64; + + s->regs[R_IMR] &= ~val; + imr_update_irq(s); + return 0; +} + +static uint64_t idr_prew(RegisterInfo *reg, uint64_t val64) +{ + XlnxVersalPmcIouSlcr *s = XILINX_VERSAL_PMC_IOU_SLCR(reg->opaque); + uint32_t val = val64; + + s->regs[R_IMR] |= val; + imr_update_irq(s); + return 0; +} + +static uint64_t itr_prew(RegisterInfo *reg, uint64_t val64) +{ + XlnxVersalPmcIouSlcr *s = XILINX_VERSAL_PMC_IOU_SLCR(reg->opaque); + uint32_t val = val64; + + s->regs[R_ISR] |= val; + imr_update_irq(s); + return 0; +} + +static uint64_t sd0_ctrl_reg_prew(RegisterInfo *reg, uint64_t val64) +{ + XlnxVersalPmcIouSlcr *s = XILINX_VERSAL_PMC_IOU_SLCR(reg->opaque); + uint32_t prev = ARRAY_FIELD_EX32(s->regs, SD0_CTRL_REG, SD0_EMMC_SEL); + + if (prev != (val64 & R_SD0_CTRL_REG_SD0_EMMC_SEL_MASK)) { + qemu_set_irq(s->sd_emmc_sel[0], !!val64); + } + + return val64; +} + +static uint64_t sd1_ctrl_reg_prew(RegisterInfo *reg, uint64_t val64) +{ + XlnxVersalPmcIouSlcr *s = XILINX_VERSAL_PMC_IOU_SLCR(reg->opaque); + uint32_t prev = ARRAY_FIELD_EX32(s->regs, SD1_CTRL_REG, SD1_EMMC_SEL); + + if (prev != (val64 & R_SD1_CTRL_REG_SD1_EMMC_SEL_MASK)) { + qemu_set_irq(s->sd_emmc_sel[1], !!val64); + } + + return val64; +} + +static uint64_t ospi_qspi_iou_axi_mux_sel_prew(RegisterInfo *reg, + uint64_t val64) +{ + XlnxVersalPmcIouSlcr *s = XILINX_VERSAL_PMC_IOU_SLCR(reg->opaque); + uint32_t val32 = (uint32_t) val64; + uint8_t ospi_mux_sel = FIELD_EX32(val32, OSPI_QSPI_IOU_AXI_MUX_SEL, + OSPI_MUX_SEL); + uint8_t qspi_ospi_mux_sel = FIELD_EX32(val32, OSPI_QSPI_IOU_AXI_MUX_SEL, + QSPI_OSPI_MUX_SEL); + + if (ospi_mux_sel != + ARRAY_FIELD_EX32(s->regs, OSPI_QSPI_IOU_AXI_MUX_SEL, OSPI_MUX_SEL)) { + qemu_set_irq(s->ospi_mux_sel, !!ospi_mux_sel); + } + + if (qspi_ospi_mux_sel != + ARRAY_FIELD_EX32(s->regs, OSPI_QSPI_IOU_AXI_MUX_SEL, + QSPI_OSPI_MUX_SEL)) { + qemu_set_irq(s->qspi_ospi_mux_sel, !!qspi_ospi_mux_sel); + } + + return val64; +} + +static RegisterAccessInfo pmc_iou_slcr_regs_info[] = { + { .name = "MIO_PIN_0", .addr = A_MIO_PIN_0, + .rsvd = 0xfffffc01, + },{ .name = "MIO_PIN_1", .addr = A_MIO_PIN_1, + .rsvd = 0xfffffc01, + },{ .name = "MIO_PIN_2", .addr = A_MIO_PIN_2, + .rsvd = 0xfffffc01, + },{ .name = "MIO_PIN_3", .addr = A_MIO_PIN_3, + .rsvd = 0xfffffc01, + },{ .name = "MIO_PIN_4", .addr = A_MIO_PIN_4, + .rsvd = 0xfffffc01, + },{ .name = "MIO_PIN_5", .addr = A_MIO_PIN_5, + .rsvd = 0xfffffc01, + },{ .name = "MIO_PIN_6", .addr = A_MIO_PIN_6, + .rsvd = 0xfffffc01, + },{ .name = "MIO_PIN_7", .addr = A_MIO_PIN_7, + .rsvd = 0xfffffc01, + },{ .name = "MIO_PIN_8", .addr = A_MIO_PIN_8, + .rsvd = 0xfffffc01, + },{ .name = "MIO_PIN_9", .addr = A_MIO_PIN_9, + .rsvd = 0xfffffc01, + },{ .name = "MIO_PIN_10", .addr = A_MIO_PIN_10, + .rsvd = 0xfffffc01, + },{ .name = "MIO_PIN_11", .addr = A_MIO_PIN_11, + .rsvd = 0xfffffc01, + },{ .name = "MIO_PIN_12", .addr = A_MIO_PIN_12, + .rsvd = 0xfffffc01, + },{ .name = "MIO_PIN_13", .addr = A_MIO_PIN_13, + .rsvd = 0xfffffc01, + },{ .name = "MIO_PIN_14", .addr = A_MIO_PIN_14, + .rsvd = 0xfffffc01, + },{ .name = "MIO_PIN_15", .addr = A_MIO_PIN_15, + .rsvd = 0xfffffc01, + },{ .name = "MIO_PIN_16", .addr = A_MIO_PIN_16, + .rsvd = 0xfffffc01, + },{ .name = "MIO_PIN_17", .addr = A_MIO_PIN_17, + .rsvd = 0xfffffc01, + },{ .name = "MIO_PIN_18", .addr = A_MIO_PIN_18, + .rsvd = 0xfffffc01, + },{ .name = "MIO_PIN_19", .addr = A_MIO_PIN_19, + .rsvd = 0xfffffc01, + },{ .name = "MIO_PIN_20", .addr = A_MIO_PIN_20, + .rsvd = 0xfffffc01, + },{ .name = "MIO_PIN_21", .addr = A_MIO_PIN_21, + .rsvd = 0xfffffc01, + },{ .name = "MIO_PIN_22", .addr = A_MIO_PIN_22, + .rsvd = 0xfffffc01, + },{ .name = "MIO_PIN_23", .addr = A_MIO_PIN_23, + .rsvd = 0xfffffc01, + },{ .name = "MIO_PIN_24", .addr = A_MIO_PIN_24, + .rsvd = 0xfffffc01, + },{ .name = "MIO_PIN_25", .addr = A_MIO_PIN_25, + .rsvd = 0xfffffc01, + },{ .name = "MIO_PIN_26", .addr = A_MIO_PIN_26, + .rsvd = 0xfffffc01, + },{ .name = "MIO_PIN_27", .addr = A_MIO_PIN_27, + .rsvd = 0xfffffc01, + },{ .name = "MIO_PIN_28", .addr = A_MIO_PIN_28, + .rsvd = 0xfffffc01, + },{ .name = "MIO_PIN_29", .addr = A_MIO_PIN_29, + .rsvd = 0xfffffc01, + },{ .name = "MIO_PIN_30", .addr = A_MIO_PIN_30, + .rsvd = 0xfffffc01, + },{ .name = "MIO_PIN_31", .addr = A_MIO_PIN_31, + .rsvd = 0xfffffc01, + },{ .name = "MIO_PIN_32", .addr = A_MIO_PIN_32, + .rsvd = 0xfffffc01, + },{ .name = "MIO_PIN_33", .addr = A_MIO_PIN_33, + .rsvd = 0xfffffc01, + },{ .name = "MIO_PIN_34", .addr = A_MIO_PIN_34, + .rsvd = 0xfffffc01, + },{ .name = "MIO_PIN_35", .addr = A_MIO_PIN_35, + .rsvd = 0xfffffc01, + },{ .name = "MIO_PIN_36", .addr = A_MIO_PIN_36, + .rsvd = 0xfffffc01, + },{ .name = "MIO_PIN_37", .addr = A_MIO_PIN_37, + .rsvd = 0xfffffc01, + },{ .name = "MIO_PIN_38", .addr = A_MIO_PIN_38, + .rsvd = 0xfffffc01, + },{ .name = "MIO_PIN_39", .addr = A_MIO_PIN_39, + .rsvd = 0xfffffc01, + },{ .name = "MIO_PIN_40", .addr = A_MIO_PIN_40, + .rsvd = 0xfffffc01, + },{ .name = "MIO_PIN_41", .addr = A_MIO_PIN_41, + .rsvd = 0xfffffc01, + },{ .name = "MIO_PIN_42", .addr = A_MIO_PIN_42, + .rsvd = 0xfffffc01, + },{ .name = "MIO_PIN_43", .addr = A_MIO_PIN_43, + .rsvd = 0xfffffc01, + },{ .name = "MIO_PIN_44", .addr = A_MIO_PIN_44, + .rsvd = 0xfffffc01, + },{ .name = "MIO_PIN_45", .addr = A_MIO_PIN_45, + .rsvd = 0xfffffc01, + },{ .name = "MIO_PIN_46", .addr = A_MIO_PIN_46, + .rsvd = 0xfffffc01, + },{ .name = "MIO_PIN_47", .addr = A_MIO_PIN_47, + .rsvd = 0xfffffc01, + },{ .name = "MIO_PIN_48", .addr = A_MIO_PIN_48, + .rsvd = 0xfffffc01, + },{ .name = "MIO_PIN_49", .addr = A_MIO_PIN_49, + .rsvd = 0xfffffc01, + },{ .name = "MIO_PIN_50", .addr = A_MIO_PIN_50, + .rsvd = 0xfffffc01, + },{ .name = "MIO_PIN_51", .addr = A_MIO_PIN_51, + .rsvd = 0xfffffc01, + },{ .name = "BNK0_EN_RX", .addr = A_BNK0_EN_RX, + .reset = 0x3ffffff, + .rsvd = 0xfc000000, + },{ .name = "BNK0_SEL_RX0", .addr = A_BNK0_SEL_RX0, + .reset = 0xffffffff, + },{ .name = "BNK0_SEL_RX1", .addr = A_BNK0_SEL_RX1, + .reset = 0xfffff, + .rsvd = 0xfff00000, + },{ .name = "BNK0_EN_RX_SCHMITT_HYST", .addr = A_BNK0_EN_RX_SCHMITT_HYST, + .rsvd = 0xfc000000, + },{ .name = "BNK0_EN_WK_PD", .addr = A_BNK0_EN_WK_PD, + .rsvd = 0xfc000000, + },{ .name = "BNK0_EN_WK_PU", .addr = A_BNK0_EN_WK_PU, + .reset = 0x3ffffff, + .rsvd = 0xfc000000, + },{ .name = "BNK0_SEL_DRV0", .addr = A_BNK0_SEL_DRV0, + .reset = 0xffffffff, + },{ .name = "BNK0_SEL_DRV1", .addr = A_BNK0_SEL_DRV1, + .reset = 0xfffff, + .rsvd = 0xfff00000, + },{ .name = "BNK0_SEL_SLEW", .addr = A_BNK0_SEL_SLEW, + .rsvd = 0xfc000000, + },{ .name = "BNK0_EN_DFT_OPT_INV", .addr = A_BNK0_EN_DFT_OPT_INV, + .rsvd = 0xfc000000, + },{ .name = "BNK0_EN_PAD2PAD_LOOPBACK", + .addr = A_BNK0_EN_PAD2PAD_LOOPBACK, + .rsvd = 0xffffe000, + },{ .name = "BNK0_RX_SPARE0", .addr = A_BNK0_RX_SPARE0, + },{ .name = "BNK0_RX_SPARE1", .addr = A_BNK0_RX_SPARE1, + .rsvd = 0xfff00000, + },{ .name = "BNK0_TX_SPARE0", .addr = A_BNK0_TX_SPARE0, + },{ .name = "BNK0_TX_SPARE1", .addr = A_BNK0_TX_SPARE1, + .rsvd = 0xfff00000, + },{ .name = "BNK0_SEL_EN1P8", .addr = A_BNK0_SEL_EN1P8, + .rsvd = 0xfffffffe, + },{ .name = "BNK0_EN_B_POR_DETECT", .addr = A_BNK0_EN_B_POR_DETECT, + .rsvd = 0xfffffffe, + },{ .name = "BNK0_LPF_BYP_POR_DETECT", .addr = A_BNK0_LPF_BYP_POR_DETECT, + .reset = 0x1, + .rsvd = 0xfffffffe, + },{ .name = "BNK0_EN_LATCH", .addr = A_BNK0_EN_LATCH, + .rsvd = 0xfffffffe, + },{ .name = "BNK0_VBG_LPF_BYP_B", .addr = A_BNK0_VBG_LPF_BYP_B, + .reset = 0x1, + .rsvd = 0xfffffffe, + },{ .name = "BNK0_EN_AMP_B", .addr = A_BNK0_EN_AMP_B, + .rsvd = 0xfffffffc, + },{ .name = "BNK0_SPARE_BIAS", .addr = A_BNK0_SPARE_BIAS, + .rsvd = 0xfffffff0, + },{ .name = "BNK0_DRIVER_BIAS", .addr = A_BNK0_DRIVER_BIAS, + .rsvd = 0xffff8000, + },{ .name = "BNK0_VMODE", .addr = A_BNK0_VMODE, + .rsvd = 0xfffffffe, + .ro = 0x1, + },{ .name = "BNK0_SEL_AUX_IO_RX", .addr = A_BNK0_SEL_AUX_IO_RX, + .rsvd = 0xfc000000, + },{ .name = "BNK0_EN_TX_HS_MODE", .addr = A_BNK0_EN_TX_HS_MODE, + .rsvd = 0xfc000000, + },{ .name = "MIO_MST_TRI0", .addr = A_MIO_MST_TRI0, + .reset = 0x3ffffff, + .rsvd = 0xfc000000, + },{ .name = "MIO_MST_TRI1", .addr = A_MIO_MST_TRI1, + .reset = 0x3ffffff, + .rsvd = 0xfc000000, + },{ .name = "BNK1_EN_RX", .addr = A_BNK1_EN_RX, + .reset = 0x3ffffff, + .rsvd = 0xfc000000, + },{ .name = "BNK1_SEL_RX0", .addr = A_BNK1_SEL_RX0, + .reset = 0xffffffff, + },{ .name = "BNK1_SEL_RX1", .addr = A_BNK1_SEL_RX1, + .reset = 0xfffff, + .rsvd = 0xfff00000, + },{ .name = "BNK1_EN_RX_SCHMITT_HYST", .addr = A_BNK1_EN_RX_SCHMITT_HYST, + .rsvd = 0xfc000000, + },{ .name = "BNK1_EN_WK_PD", .addr = A_BNK1_EN_WK_PD, + .rsvd = 0xfc000000, + },{ .name = "BNK1_EN_WK_PU", .addr = A_BNK1_EN_WK_PU, + .reset = 0x3ffffff, + .rsvd = 0xfc000000, + },{ .name = "BNK1_SEL_DRV0", .addr = A_BNK1_SEL_DRV0, + .reset = 0xffffffff, + },{ .name = "BNK1_SEL_DRV1", .addr = A_BNK1_SEL_DRV1, + .reset = 0xfffff, + .rsvd = 0xfff00000, + },{ .name = "BNK1_SEL_SLEW", .addr = A_BNK1_SEL_SLEW, + .rsvd = 0xfc000000, + },{ .name = "BNK1_EN_DFT_OPT_INV", .addr = A_BNK1_EN_DFT_OPT_INV, + .rsvd = 0xfc000000, + },{ .name = "BNK1_EN_PAD2PAD_LOOPBACK", + .addr = A_BNK1_EN_PAD2PAD_LOOPBACK, + .rsvd = 0xffffe000, + },{ .name = "BNK1_RX_SPARE0", .addr = A_BNK1_RX_SPARE0, + },{ .name = "BNK1_RX_SPARE1", .addr = A_BNK1_RX_SPARE1, + .rsvd = 0xfff00000, + },{ .name = "BNK1_TX_SPARE0", .addr = A_BNK1_TX_SPARE0, + },{ .name = "BNK1_TX_SPARE1", .addr = A_BNK1_TX_SPARE1, + .rsvd = 0xfff00000, + },{ .name = "BNK1_SEL_EN1P8", .addr = A_BNK1_SEL_EN1P8, + .rsvd = 0xfffffffe, + },{ .name = "BNK1_EN_B_POR_DETECT", .addr = A_BNK1_EN_B_POR_DETECT, + .rsvd = 0xfffffffe, + },{ .name = "BNK1_LPF_BYP_POR_DETECT", .addr = A_BNK1_LPF_BYP_POR_DETECT, + .reset = 0x1, + .rsvd = 0xfffffffe, + },{ .name = "BNK1_EN_LATCH", .addr = A_BNK1_EN_LATCH, + .rsvd = 0xfffffffe, + },{ .name = "BNK1_VBG_LPF_BYP_B", .addr = A_BNK1_VBG_LPF_BYP_B, + .reset = 0x1, + .rsvd = 0xfffffffe, + },{ .name = "BNK1_EN_AMP_B", .addr = A_BNK1_EN_AMP_B, + .rsvd = 0xfffffffc, + },{ .name = "BNK1_SPARE_BIAS", .addr = A_BNK1_SPARE_BIAS, + .rsvd = 0xfffffff0, + },{ .name = "BNK1_DRIVER_BIAS", .addr = A_BNK1_DRIVER_BIAS, + .rsvd = 0xffff8000, + },{ .name = "BNK1_VMODE", .addr = A_BNK1_VMODE, + .rsvd = 0xfffffffe, + .ro = 0x1, + },{ .name = "BNK1_SEL_AUX_IO_RX", .addr = A_BNK1_SEL_AUX_IO_RX, + .rsvd = 0xfc000000, + },{ .name = "BNK1_EN_TX_HS_MODE", .addr = A_BNK1_EN_TX_HS_MODE, + .rsvd = 0xfc000000, + },{ .name = "SD0_CLK_CTRL", .addr = A_SD0_CLK_CTRL, + .rsvd = 0xfffffff8, + },{ .name = "SD0_CTRL_REG", .addr = A_SD0_CTRL_REG, + .rsvd = 0xfffffffe, + .pre_write = sd0_ctrl_reg_prew, + },{ .name = "SD0_CONFIG_REG1", .addr = A_SD0_CONFIG_REG1, + .reset = 0x3250, + .rsvd = 0xffff8000, + },{ .name = "SD0_CONFIG_REG2", .addr = A_SD0_CONFIG_REG2, + .reset = 0xffc, + .rsvd = 0xffffc000, + },{ .name = "SD0_CONFIG_REG3", .addr = A_SD0_CONFIG_REG3, + .reset = 0x407, + .rsvd = 0xfffff800, + },{ .name = "SD0_INITPRESET", .addr = A_SD0_INITPRESET, + .reset = 0x100, + .rsvd = 0xffffe000, + },{ .name = "SD0_DSPPRESET", .addr = A_SD0_DSPPRESET, + .reset = 0x4, + .rsvd = 0xffffe000, + },{ .name = "SD0_HSPDPRESET", .addr = A_SD0_HSPDPRESET, + .reset = 0x2, + .rsvd = 0xffffe000, + },{ .name = "SD0_SDR12PRESET", .addr = A_SD0_SDR12PRESET, + .reset = 0x4, + .rsvd = 0xffffe000, + },{ .name = "SD0_SDR25PRESET", .addr = A_SD0_SDR25PRESET, + .reset = 0x2, + .rsvd = 0xffffe000, + },{ .name = "SD0_SDR50PRSET", .addr = A_SD0_SDR50PRSET, + .reset = 0x1, + .rsvd = 0xffffe000, + },{ .name = "SD0_SDR104PRST", .addr = A_SD0_SDR104PRST, + .rsvd = 0xffffe000, + },{ .name = "SD0_DDR50PRESET", .addr = A_SD0_DDR50PRESET, + .reset = 0x2, + .rsvd = 0xffffe000, + },{ .name = "SD0_MAXCUR1P8", .addr = A_SD0_MAXCUR1P8, + .rsvd = 0xffffff00, + },{ .name = "SD0_MAXCUR3P0", .addr = A_SD0_MAXCUR3P0, + .rsvd = 0xffffff00, + },{ .name = "SD0_MAXCUR3P3", .addr = A_SD0_MAXCUR3P3, + .rsvd = 0xffffff00, + },{ .name = "SD0_DLL_CTRL", .addr = A_SD0_DLL_CTRL, + .reset = 0x1, + .rsvd = 0xfffffc00, + .ro = 0x19, + },{ .name = "SD0_CDN_CTRL", .addr = A_SD0_CDN_CTRL, + .rsvd = 0xfffffffe, + },{ .name = "SD0_DLL_TEST", .addr = A_SD0_DLL_TEST, + .rsvd = 0xff000000, + },{ .name = "SD0_RX_TUNING_SEL", .addr = A_SD0_RX_TUNING_SEL, + .rsvd = 0xfffffe00, + .ro = 0x1ff, + },{ .name = "SD0_DLL_DIV_MAP0", .addr = A_SD0_DLL_DIV_MAP0, + .reset = 0x50505050, + },{ .name = "SD0_DLL_DIV_MAP1", .addr = A_SD0_DLL_DIV_MAP1, + .reset = 0x50505050, + },{ .name = "SD0_IOU_COHERENT_CTRL", .addr = A_SD0_IOU_COHERENT_CTRL, + .rsvd = 0xfffffff0, + },{ .name = "SD0_IOU_INTERCONNECT_ROUTE", + .addr = A_SD0_IOU_INTERCONNECT_ROUTE, + .rsvd = 0xfffffffe, + },{ .name = "SD0_IOU_RAM", .addr = A_SD0_IOU_RAM, + .reset = 0x24, + .rsvd = 0xffffff80, + },{ .name = "SD0_IOU_INTERCONNECT_QOS", + .addr = A_SD0_IOU_INTERCONNECT_QOS, + .rsvd = 0xfffffff0, + },{ .name = "SD1_CLK_CTRL", .addr = A_SD1_CLK_CTRL, + .rsvd = 0xfffffffc, + },{ .name = "SD1_CTRL_REG", .addr = A_SD1_CTRL_REG, + .rsvd = 0xfffffffe, + .pre_write = sd1_ctrl_reg_prew, + },{ .name = "SD1_CONFIG_REG1", .addr = A_SD1_CONFIG_REG1, + .reset = 0x3250, + .rsvd = 0xffff8000, + },{ .name = "SD1_CONFIG_REG2", .addr = A_SD1_CONFIG_REG2, + .reset = 0xffc, + .rsvd = 0xffffc000, + },{ .name = "SD1_CONFIG_REG3", .addr = A_SD1_CONFIG_REG3, + .reset = 0x407, + .rsvd = 0xfffff800, + },{ .name = "SD1_INITPRESET", .addr = A_SD1_INITPRESET, + .reset = 0x100, + .rsvd = 0xffffe000, + },{ .name = "SD1_DSPPRESET", .addr = A_SD1_DSPPRESET, + .reset = 0x4, + .rsvd = 0xffffe000, + },{ .name = "SD1_HSPDPRESET", .addr = A_SD1_HSPDPRESET, + .reset = 0x2, + .rsvd = 0xffffe000, + },{ .name = "SD1_SDR12PRESET", .addr = A_SD1_SDR12PRESET, + .reset = 0x4, + .rsvd = 0xffffe000, + },{ .name = "SD1_SDR25PRESET", .addr = A_SD1_SDR25PRESET, + .reset = 0x2, + .rsvd = 0xffffe000, + },{ .name = "SD1_SDR50PRSET", .addr = A_SD1_SDR50PRSET, + .reset = 0x1, + .rsvd = 0xffffe000, + },{ .name = "SD1_SDR104PRST", .addr = A_SD1_SDR104PRST, + .rsvd = 0xffffe000, + },{ .name = "SD1_DDR50PRESET", .addr = A_SD1_DDR50PRESET, + .reset = 0x2, + .rsvd = 0xffffe000, + },{ .name = "SD1_MAXCUR1P8", .addr = A_SD1_MAXCUR1P8, + .rsvd = 0xffffff00, + },{ .name = "SD1_MAXCUR3P0", .addr = A_SD1_MAXCUR3P0, + .rsvd = 0xffffff00, + },{ .name = "SD1_MAXCUR3P3", .addr = A_SD1_MAXCUR3P3, + .rsvd = 0xffffff00, + },{ .name = "SD1_DLL_CTRL", .addr = A_SD1_DLL_CTRL, + .reset = 0x1, + .rsvd = 0xfffffc00, + .ro = 0x19, + },{ .name = "SD1_CDN_CTRL", .addr = A_SD1_CDN_CTRL, + .rsvd = 0xfffffffe, + },{ .name = "SD1_DLL_TEST", .addr = A_SD1_DLL_TEST, + .rsvd = 0xff000000, + },{ .name = "SD1_RX_TUNING_SEL", .addr = A_SD1_RX_TUNING_SEL, + .rsvd = 0xfffffe00, + .ro = 0x1ff, + },{ .name = "SD1_DLL_DIV_MAP0", .addr = A_SD1_DLL_DIV_MAP0, + .reset = 0x50505050, + },{ .name = "SD1_DLL_DIV_MAP1", .addr = A_SD1_DLL_DIV_MAP1, + .reset = 0x50505050, + },{ .name = "SD1_IOU_COHERENT_CTRL", .addr = A_SD1_IOU_COHERENT_CTRL, + .rsvd = 0xfffffff0, + },{ .name = "SD1_IOU_INTERCONNECT_ROUTE", + .addr = A_SD1_IOU_INTERCONNECT_ROUTE, + .rsvd = 0xfffffffe, + },{ .name = "SD1_IOU_RAM", .addr = A_SD1_IOU_RAM, + .reset = 0x24, + .rsvd = 0xffffff80, + },{ .name = "SD1_IOU_INTERCONNECT_QOS", + .addr = A_SD1_IOU_INTERCONNECT_QOS, + .rsvd = 0xfffffff0, + },{ .name = "OSPI_QSPI_IOU_AXI_MUX_SEL", + .addr = A_OSPI_QSPI_IOU_AXI_MUX_SEL, + .reset = 0x1, + .rsvd = 0xfffffffc, + .pre_write = ospi_qspi_iou_axi_mux_sel_prew, + },{ .name = "QSPI_IOU_COHERENT_CTRL", .addr = A_QSPI_IOU_COHERENT_CTRL, + .rsvd = 0xfffffff0, + },{ .name = "QSPI_IOU_INTERCONNECT_ROUTE", + .addr = A_QSPI_IOU_INTERCONNECT_ROUTE, + .rsvd = 0xfffffffe, + },{ .name = "QSPI_IOU_RAM", .addr = A_QSPI_IOU_RAM, + .reset = 0x1224, + .rsvd = 0xffffc000, + },{ .name = "QSPI_IOU_INTERCONNECT_QOS", + .addr = A_QSPI_IOU_INTERCONNECT_QOS, + .rsvd = 0xfffffff0, + },{ .name = "OSPI_IOU_COHERENT_CTRL", .addr = A_OSPI_IOU_COHERENT_CTRL, + .rsvd = 0xfffffff0, + },{ .name = "OSPI_IOU_INTERCONNECT_ROUTE", + .addr = A_OSPI_IOU_INTERCONNECT_ROUTE, + .rsvd = 0xfffffffe, + },{ .name = "OSPI_IOU_RAM", .addr = A_OSPI_IOU_RAM, + .reset = 0xa, + .rsvd = 0xffffffc0, + },{ .name = "OSPI_IOU_INTERCONNECT_QOS", + .addr = A_OSPI_IOU_INTERCONNECT_QOS, + .rsvd = 0xfffffff0, + },{ .name = "OSPI_REFCLK_DLY_CTRL", .addr = A_OSPI_REFCLK_DLY_CTRL, + .reset = 0x13, + .rsvd = 0xffffffe0, + },{ .name = "CUR_PWR_ST", .addr = A_CUR_PWR_ST, + .rsvd = 0xfffffffc, + .ro = 0x3, + },{ .name = "CONNECT_ST", .addr = A_CONNECT_ST, + .rsvd = 0xfffffffe, + .ro = 0x1, + },{ .name = "PW_STATE_REQ", .addr = A_PW_STATE_REQ, + .rsvd = 0xfffffffc, + },{ .name = "HOST_U2_PORT_DISABLE", .addr = A_HOST_U2_PORT_DISABLE, + .rsvd = 0xfffffffe, + },{ .name = "DBG_U2PMU", .addr = A_DBG_U2PMU, + .ro = 0xffffffff, + },{ .name = "DBG_U2PMU_EXT1", .addr = A_DBG_U2PMU_EXT1, + .ro = 0xffffffff, + },{ .name = "DBG_U2PMU_EXT2", .addr = A_DBG_U2PMU_EXT2, + .rsvd = 0xfffffff0, + .ro = 0xf, + },{ .name = "PME_GEN_U2PMU", .addr = A_PME_GEN_U2PMU, + .rsvd = 0xfffffffe, + .ro = 0x1, + },{ .name = "PWR_CONFIG_USB2", .addr = A_PWR_CONFIG_USB2, + .rsvd = 0xc0000000, + },{ .name = "PHY_HUB", .addr = A_PHY_HUB, + .rsvd = 0xfffffffc, + .ro = 0x2, + },{ .name = "CTRL", .addr = A_CTRL, + },{ .name = "ISR", .addr = A_ISR, + .w1c = 0x1, + .post_write = isr_postw, + },{ .name = "IMR", .addr = A_IMR, + .reset = 0x1, + .ro = 0x1, + },{ .name = "IER", .addr = A_IER, + .pre_write = ier_prew, + },{ .name = "IDR", .addr = A_IDR, + .pre_write = idr_prew, + },{ .name = "ITR", .addr = A_ITR, + .pre_write = itr_prew, + },{ .name = "PARITY_ISR", .addr = A_PARITY_ISR, + .w1c = 0x1fff, + .post_write = parity_isr_postw, + },{ .name = "PARITY_IMR", .addr = A_PARITY_IMR, + .reset = 0x1fff, + .ro = 0x1fff, + },{ .name = "PARITY_IER", .addr = A_PARITY_IER, + .pre_write = parity_ier_prew, + },{ .name = "PARITY_IDR", .addr = A_PARITY_IDR, + .pre_write = parity_idr_prew, + },{ .name = "PARITY_ITR", .addr = A_PARITY_ITR, + .pre_write = parity_itr_prew, + },{ .name = "WPROT0", .addr = A_WPROT0, + .reset = 0x1, + } +}; + +static void xlnx_versal_pmc_iou_slcr_reset_init(Object *obj, ResetType type) +{ + XlnxVersalPmcIouSlcr *s = XILINX_VERSAL_PMC_IOU_SLCR(obj); + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(s->regs_info); ++i) { + register_reset(&s->regs_info[i]); + } +} + +static void xlnx_versal_pmc_iou_slcr_reset_hold(Object *obj) +{ + XlnxVersalPmcIouSlcr *s = XILINX_VERSAL_PMC_IOU_SLCR(obj); + + parity_imr_update_irq(s); + imr_update_irq(s); + + /* + * Setup OSPI_QSPI mux + * By default axi slave interface is enabled for ospi-dma + */ + qemu_set_irq(s->ospi_mux_sel, 0); + qemu_set_irq(s->qspi_ospi_mux_sel, 1); +} + +static const MemoryRegionOps pmc_iou_slcr_ops = { + .read = register_read_memory, + .write = register_write_memory, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static void xlnx_versal_pmc_iou_slcr_realize(DeviceState *dev, Error **errp) +{ + XlnxVersalPmcIouSlcr *s = XILINX_VERSAL_PMC_IOU_SLCR(dev); + + qdev_init_gpio_out_named(dev, s->sd_emmc_sel, "sd-emmc-sel", 2); + qdev_init_gpio_out_named(dev, &s->qspi_ospi_mux_sel, + "qspi-ospi-mux-sel", 1); + qdev_init_gpio_out_named(dev, &s->ospi_mux_sel, "ospi-mux-sel", 1); +} + +static void xlnx_versal_pmc_iou_slcr_init(Object *obj) +{ + XlnxVersalPmcIouSlcr *s = XILINX_VERSAL_PMC_IOU_SLCR(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + RegisterInfoArray *reg_array; + + memory_region_init(&s->iomem, obj, TYPE_XILINX_VERSAL_PMC_IOU_SLCR, + XILINX_VERSAL_PMC_IOU_SLCR_R_MAX * 4); + reg_array = + register_init_block32(DEVICE(obj), pmc_iou_slcr_regs_info, + ARRAY_SIZE(pmc_iou_slcr_regs_info), + s->regs_info, s->regs, + &pmc_iou_slcr_ops, + XILINX_VERSAL_PMC_IOU_SLCR_ERR_DEBUG, + XILINX_VERSAL_PMC_IOU_SLCR_R_MAX * 4); + memory_region_add_subregion(&s->iomem, + 0x0, + ®_array->mem); + sysbus_init_mmio(sbd, &s->iomem); + sysbus_init_irq(sbd, &s->irq_parity_imr); + sysbus_init_irq(sbd, &s->irq_imr); +} + +static const VMStateDescription vmstate_pmc_iou_slcr = { + .name = TYPE_XILINX_VERSAL_PMC_IOU_SLCR, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(regs, XlnxVersalPmcIouSlcr, + XILINX_VERSAL_PMC_IOU_SLCR_R_MAX), + VMSTATE_END_OF_LIST(), + } +}; + +static void xlnx_versal_pmc_iou_slcr_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + + dc->realize = xlnx_versal_pmc_iou_slcr_realize; + dc->vmsd = &vmstate_pmc_iou_slcr; + rc->phases.enter = xlnx_versal_pmc_iou_slcr_reset_init; + rc->phases.hold = xlnx_versal_pmc_iou_slcr_reset_hold; +} + +static const TypeInfo xlnx_versal_pmc_iou_slcr_info = { + .name = TYPE_XILINX_VERSAL_PMC_IOU_SLCR, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(XlnxVersalPmcIouSlcr), + .class_init = xlnx_versal_pmc_iou_slcr_class_init, + .instance_init = xlnx_versal_pmc_iou_slcr_init, +}; + +static void xlnx_versal_pmc_iou_slcr_register_types(void) +{ + type_register_static(&xlnx_versal_pmc_iou_slcr_info); +} + +type_init(xlnx_versal_pmc_iou_slcr_register_types) diff --git a/include/hw/misc/xlnx-versal-pmc-iou-slcr.h b/include/hw/misc/xlnx-versal-pmc-iou-slcr.h new file mode 100644 index 0000000000..ab4e4b4f18 --- /dev/null +++ b/include/hw/misc/xlnx-versal-pmc-iou-slcr.h @@ -0,0 +1,78 @@ +/* + * Header file for the Xilinx Versal's PMC IOU SLCR + * + * Copyright (C) 2021 Xilinx Inc + * Written by 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. + */ + +/* + * This is a model of Xilinx Versal's PMC I/O Peripheral Control and Status + * module documented in Versal's Technical Reference manual [1] and the Versal + * ACAP Register reference [2]. + * + * References: + * + * [1] Versal ACAP Technical Reference Manual, + * https://www.xilinx.com/support/documentation/architecture-manuals/am011-versal-acap-trm.pdf + * + * [2] Versal ACAP Register Reference, + * https://www.xilinx.com/html_docs/registers/am012/am012-versal-register-reference.html#mod___pmc_iop_slcr.html + * + * QEMU interface: + * + sysbus MMIO region 0: MemoryRegion for the device's registers + * + sysbus IRQ 0: PMC (AXI and APB) parity error interrupt detected by the PMC + * I/O peripherals. + * + sysbus IRQ 1: Device interrupt. + * + Named GPIO output "sd-emmc-sel[0]": Enables 0: SD mode or 1: eMMC mode on + * SD/eMMC controller 0. + * + Named GPIO output "sd-emmc-sel[1]": Enables 0: SD mode or 1: eMMC mode on + * SD/eMMC controller 1. + * + Named GPIO output "qspi-ospi-mux-sel": Selects 0: QSPI linear region or 1: + * OSPI linear region. + * + Named GPIO output "ospi-mux-sel": Selects 0: OSPI Indirect access mode or + * 1: OSPI direct access mode. + */ + +#ifndef XILINX_VERSAL_PMC_IOU_SLCR_H +#define XILINX_VERSAL_PMC_IOU_SLCR_H + +#include "hw/register.h" + +#define TYPE_XILINX_VERSAL_PMC_IOU_SLCR "xlnx.versal-pmc-iou-slcr" + +OBJECT_DECLARE_SIMPLE_TYPE(XlnxVersalPmcIouSlcr, XILINX_VERSAL_PMC_IOU_SLCR) + +#define XILINX_VERSAL_PMC_IOU_SLCR_R_MAX (0x828 / 4 + 1) + +struct XlnxVersalPmcIouSlcr { + SysBusDevice parent_obj; + MemoryRegion iomem; + qemu_irq irq_parity_imr; + qemu_irq irq_imr; + qemu_irq sd_emmc_sel[2]; + qemu_irq qspi_ospi_mux_sel; + qemu_irq ospi_mux_sel; + + uint32_t regs[XILINX_VERSAL_PMC_IOU_SLCR_R_MAX]; + RegisterInfo regs_info[XILINX_VERSAL_PMC_IOU_SLCR_R_MAX]; +}; + +#endif /* XILINX_VERSAL_PMC_IOU_SLCR_H */ From 9a6d491831e6e12a971f6a0c59e9553e96a9241c Mon Sep 17 00:00:00 2001 From: Francisco Iglesias Date: Fri, 21 Jan 2022 16:11:33 +0000 Subject: [PATCH 077/460] hw/arm/xlnx-versal: 'Or' the interrupts from the BBRAM and RTC models Add an orgate and 'or' the interrupts from the BBRAM and RTC models. Signed-off-by: Francisco Iglesias Reviewed-by: Peter Maydell Reviewed-by: Luc Michel Message-id: 20220121161141.14389-3-francisco.iglesias@xilinx.com Signed-off-by: Peter Maydell --- hw/arm/xlnx-versal-virt.c | 2 +- hw/arm/xlnx-versal.c | 28 ++++++++++++++++++++++++++-- include/hw/arm/xlnx-versal.h | 5 +++-- 3 files changed, 30 insertions(+), 5 deletions(-) diff --git a/hw/arm/xlnx-versal-virt.c b/hw/arm/xlnx-versal-virt.c index 0c5edc898e..8ea9979710 100644 --- a/hw/arm/xlnx-versal-virt.c +++ b/hw/arm/xlnx-versal-virt.c @@ -365,7 +365,7 @@ static void fdt_add_bbram_node(VersalVirt *s) qemu_fdt_add_subnode(s->fdt, name); qemu_fdt_setprop_cells(s->fdt, name, "interrupts", - GIC_FDT_IRQ_TYPE_SPI, VERSAL_BBRAM_APB_IRQ_0, + GIC_FDT_IRQ_TYPE_SPI, VERSAL_PMC_APB_IRQ, GIC_FDT_IRQ_FLAGS_LEVEL_HI); qemu_fdt_setprop(s->fdt, name, "interrupt-names", interrupt_names, sizeof(interrupt_names)); diff --git a/hw/arm/xlnx-versal.c b/hw/arm/xlnx-versal.c index b2705b6925..fefd00b57c 100644 --- a/hw/arm/xlnx-versal.c +++ b/hw/arm/xlnx-versal.c @@ -25,6 +25,8 @@ #define XLNX_VERSAL_ACPU_TYPE ARM_CPU_TYPE_NAME("cortex-a72") #define GEM_REVISION 0x40070106 +#define VERSAL_NUM_PMC_APB_IRQS 2 + static void versal_create_apu_cpus(Versal *s) { int i; @@ -260,6 +262,25 @@ static void versal_create_sds(Versal *s, qemu_irq *pic) } } +static void versal_create_pmc_apb_irq_orgate(Versal *s, qemu_irq *pic) +{ + DeviceState *orgate; + + /* + * The VERSAL_PMC_APB_IRQ is an 'or' of the interrupts from the following + * models: + * - RTC + * - BBRAM + */ + object_initialize_child(OBJECT(s), "pmc-apb-irq-orgate", + &s->pmc.apb_irq_orgate, TYPE_OR_IRQ); + orgate = DEVICE(&s->pmc.apb_irq_orgate); + object_property_set_int(OBJECT(orgate), + "num-lines", VERSAL_NUM_PMC_APB_IRQS, &error_fatal); + qdev_realize(orgate, NULL, &error_fatal); + qdev_connect_gpio_out(orgate, 0, pic[VERSAL_PMC_APB_IRQ]); +} + static void versal_create_rtc(Versal *s, qemu_irq *pic) { SysBusDevice *sbd; @@ -277,7 +298,8 @@ static void versal_create_rtc(Versal *s, qemu_irq *pic) * TODO: Connect the ALARM and SECONDS interrupts once our RTC model * supports them. */ - sysbus_connect_irq(sbd, 1, pic[VERSAL_RTC_APB_ERR_IRQ]); + sysbus_connect_irq(sbd, 1, + qdev_get_gpio_in(DEVICE(&s->pmc.apb_irq_orgate), 0)); } static void versal_create_xrams(Versal *s, qemu_irq *pic) @@ -328,7 +350,8 @@ static void versal_create_bbram(Versal *s, qemu_irq *pic) sysbus_realize(sbd, &error_fatal); memory_region_add_subregion(&s->mr_ps, MM_PMC_BBRAM_CTRL, sysbus_mmio_get_region(sbd, 0)); - sysbus_connect_irq(sbd, 0, pic[VERSAL_BBRAM_APB_IRQ_0]); + sysbus_connect_irq(sbd, 0, + qdev_get_gpio_in(DEVICE(&s->pmc.apb_irq_orgate), 1)); } static void versal_realize_efuse_part(Versal *s, Object *dev, hwaddr base) @@ -455,6 +478,7 @@ static void versal_realize(DeviceState *dev, Error **errp) versal_create_gems(s, pic); versal_create_admas(s, pic); versal_create_sds(s, pic); + versal_create_pmc_apb_irq_orgate(s, pic); versal_create_rtc(s, pic); versal_create_xrams(s, pic); versal_create_bbram(s, pic); diff --git a/include/hw/arm/xlnx-versal.h b/include/hw/arm/xlnx-versal.h index 895ba12c61..62fb6f0a68 100644 --- a/include/hw/arm/xlnx-versal.h +++ b/include/hw/arm/xlnx-versal.h @@ -85,6 +85,8 @@ struct Versal { XlnxEFuse efuse; XlnxVersalEFuseCtrl efuse_ctrl; XlnxVersalEFuseCache efuse_cache; + + qemu_or_irq apb_irq_orgate; } pmc; struct { @@ -111,8 +113,7 @@ struct Versal { #define VERSAL_GEM1_WAKE_IRQ_0 59 #define VERSAL_ADMA_IRQ_0 60 #define VERSAL_XRAM_IRQ_0 79 -#define VERSAL_BBRAM_APB_IRQ_0 121 -#define VERSAL_RTC_APB_ERR_IRQ 121 +#define VERSAL_PMC_APB_IRQ 121 #define VERSAL_SD0_IRQ_0 126 #define VERSAL_EFUSE_IRQ 139 #define VERSAL_RTC_ALARM_IRQ 142 From f7c9aecbf850fd819643ea884de338f810e285f1 Mon Sep 17 00:00:00 2001 From: Francisco Iglesias Date: Fri, 21 Jan 2022 16:11:34 +0000 Subject: [PATCH 078/460] hw/arm/xlnx-versal: Connect Versal's PMC SLCR Connect Versal's PMC SLCR (system-level control registers) model. Signed-off-by: Francisco Iglesias Reviewed-by: Luc Michel Message-id: 20220121161141.14389-4-francisco.iglesias@xilinx.com Signed-off-by: Peter Maydell --- hw/arm/xlnx-versal.c | 71 +++++++++++++++++++++++++++++++++++- include/hw/arm/xlnx-versal.h | 5 +++ 2 files changed, 75 insertions(+), 1 deletion(-) diff --git a/hw/arm/xlnx-versal.c b/hw/arm/xlnx-versal.c index fefd00b57c..c8c0c102c7 100644 --- a/hw/arm/xlnx-versal.c +++ b/hw/arm/xlnx-versal.c @@ -21,11 +21,13 @@ #include "kvm_arm.h" #include "hw/misc/unimp.h" #include "hw/arm/xlnx-versal.h" +#include "qemu/log.h" +#include "hw/sysbus.h" #define XLNX_VERSAL_ACPU_TYPE ARM_CPU_TYPE_NAME("cortex-a72") #define GEM_REVISION 0x40070106 -#define VERSAL_NUM_PMC_APB_IRQS 2 +#define VERSAL_NUM_PMC_APB_IRQS 3 static void versal_create_apu_cpus(Versal *s) { @@ -271,6 +273,7 @@ static void versal_create_pmc_apb_irq_orgate(Versal *s, qemu_irq *pic) * models: * - RTC * - BBRAM + * - PMC SLCR */ object_initialize_child(OBJECT(s), "pmc-apb-irq-orgate", &s->pmc.apb_irq_orgate, TYPE_OR_IRQ); @@ -392,6 +395,23 @@ static void versal_create_efuse(Versal *s, qemu_irq *pic) sysbus_connect_irq(SYS_BUS_DEVICE(ctrl), 0, pic[VERSAL_EFUSE_IRQ]); } +static void versal_create_pmc_iou_slcr(Versal *s, qemu_irq *pic) +{ + SysBusDevice *sbd; + + object_initialize_child(OBJECT(s), "versal-pmc-iou-slcr", &s->pmc.iou.slcr, + TYPE_XILINX_VERSAL_PMC_IOU_SLCR); + + sbd = SYS_BUS_DEVICE(&s->pmc.iou.slcr); + sysbus_realize(sbd, &error_fatal); + + memory_region_add_subregion(&s->mr_ps, MM_PMC_PMC_IOU_SLCR, + sysbus_mmio_get_region(sbd, 0)); + + sysbus_connect_irq(sbd, 0, + qdev_get_gpio_in(DEVICE(&s->pmc.apb_irq_orgate), 2)); +} + /* This takes the board allocated linear DDR memory and creates aliases * for each split DDR range/aperture on the Versal address map. */ @@ -448,8 +468,31 @@ static void versal_unimp_area(Versal *s, const char *name, memory_region_add_subregion(mr, base, mr_dev); } +static void versal_unimp_sd_emmc_sel(void *opaque, int n, int level) +{ + qemu_log_mask(LOG_UNIMP, + "Selecting between enabling SD mode or eMMC mode on " + "controller %d is not yet implemented\n", n); +} + +static void versal_unimp_qspi_ospi_mux_sel(void *opaque, int n, int level) +{ + qemu_log_mask(LOG_UNIMP, + "Selecting between enabling the QSPI or OSPI linear address " + "region is not yet implemented\n"); +} + +static void versal_unimp_irq_parity_imr(void *opaque, int n, int level) +{ + qemu_log_mask(LOG_UNIMP, + "PMC SLCR parity interrupt behaviour " + "is not yet implemented\n"); +} + static void versal_unimp(Versal *s) { + qemu_irq gpio_in; + versal_unimp_area(s, "psm", &s->mr_ps, MM_PSM_START, MM_PSM_END - MM_PSM_START); versal_unimp_area(s, "crl", &s->mr_ps, @@ -464,6 +507,31 @@ static void versal_unimp(Versal *s) MM_IOU_SCNTR, MM_IOU_SCNTR_SIZE); versal_unimp_area(s, "iou-scntr-seucre", &s->mr_ps, MM_IOU_SCNTRS, MM_IOU_SCNTRS_SIZE); + + qdev_init_gpio_in_named(DEVICE(s), versal_unimp_sd_emmc_sel, + "sd-emmc-sel-dummy", 2); + qdev_init_gpio_in_named(DEVICE(s), versal_unimp_qspi_ospi_mux_sel, + "qspi-ospi-mux-sel-dummy", 1); + qdev_init_gpio_in_named(DEVICE(s), versal_unimp_irq_parity_imr, + "irq-parity-imr-dummy", 1); + + gpio_in = qdev_get_gpio_in_named(DEVICE(s), "sd-emmc-sel-dummy", 0); + qdev_connect_gpio_out_named(DEVICE(&s->pmc.iou.slcr), "sd-emmc-sel", 0, + gpio_in); + + gpio_in = qdev_get_gpio_in_named(DEVICE(s), "sd-emmc-sel-dummy", 1); + qdev_connect_gpio_out_named(DEVICE(&s->pmc.iou.slcr), "sd-emmc-sel", 1, + gpio_in); + + gpio_in = qdev_get_gpio_in_named(DEVICE(s), "qspi-ospi-mux-sel-dummy", 0); + qdev_connect_gpio_out_named(DEVICE(&s->pmc.iou.slcr), + "qspi-ospi-mux-sel", 0, + gpio_in); + + gpio_in = qdev_get_gpio_in_named(DEVICE(s), "irq-parity-imr-dummy", 0); + qdev_connect_gpio_out_named(DEVICE(&s->pmc.iou.slcr), + SYSBUS_DEVICE_GPIO_IRQ, 0, + gpio_in); } static void versal_realize(DeviceState *dev, Error **errp) @@ -483,6 +551,7 @@ static void versal_realize(DeviceState *dev, Error **errp) versal_create_xrams(s, pic); versal_create_bbram(s, pic); versal_create_efuse(s, pic); + versal_create_pmc_iou_slcr(s, pic); versal_map_ddr(s); versal_unimp(s); diff --git a/include/hw/arm/xlnx-versal.h b/include/hw/arm/xlnx-versal.h index 62fb6f0a68..811df73350 100644 --- a/include/hw/arm/xlnx-versal.h +++ b/include/hw/arm/xlnx-versal.h @@ -26,6 +26,7 @@ #include "hw/misc/xlnx-versal-xramc.h" #include "hw/nvram/xlnx-bbram.h" #include "hw/nvram/xlnx-versal-efuse.h" +#include "hw/misc/xlnx-versal-pmc-iou-slcr.h" #define TYPE_XLNX_VERSAL "xlnx-versal" OBJECT_DECLARE_SIMPLE_TYPE(Versal, XLNX_VERSAL) @@ -78,6 +79,7 @@ struct Versal { struct { struct { SDHCIState sd[XLNX_VERSAL_NR_SDS]; + XlnxVersalPmcIouSlcr slcr; } iou; XlnxZynqMPRTC rtc; @@ -179,6 +181,9 @@ struct Versal { #define MM_FPD_FPD_APU 0xfd5c0000 #define MM_FPD_FPD_APU_SIZE 0x100 +#define MM_PMC_PMC_IOU_SLCR 0xf1060000 +#define MM_PMC_PMC_IOU_SLCR_SIZE 0x10000 + #define MM_PMC_SD0 0xf1040000U #define MM_PMC_SD0_SIZE 0x10000 #define MM_PMC_BBRAM_CTRL 0xf11f0000 From ba4fbdbd9b8be7bed4285bc4cf684488b48fb518 Mon Sep 17 00:00:00 2001 From: Francisco Iglesias Date: Fri, 21 Jan 2022 16:11:35 +0000 Subject: [PATCH 079/460] include/hw/dma/xlnx_csu_dma: Add in missing includes in the header Add in the missing includes in the header for being able to build the DMA model when reusing it. Signed-off-by: Francisco Iglesias Reviewed-by: Peter Maydell Reviewed-by: Luc Michel Message-id: 20220121161141.14389-5-francisco.iglesias@xilinx.com Signed-off-by: Peter Maydell --- include/hw/dma/xlnx_csu_dma.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/hw/dma/xlnx_csu_dma.h b/include/hw/dma/xlnx_csu_dma.h index 9e9dc551e9..28806628b1 100644 --- a/include/hw/dma/xlnx_csu_dma.h +++ b/include/hw/dma/xlnx_csu_dma.h @@ -21,6 +21,11 @@ #ifndef XLNX_CSU_DMA_H #define XLNX_CSU_DMA_H +#include "hw/sysbus.h" +#include "hw/register.h" +#include "hw/ptimer.h" +#include "hw/stream.h" + #define TYPE_XLNX_CSU_DMA "xlnx.csu_dma" #define XLNX_CSU_DMA_R_MAX (0x2c / 4) From 00f05c02f9e7342fb423110061bdf66921fe80b2 Mon Sep 17 00:00:00 2001 From: Francisco Iglesias Date: Fri, 21 Jan 2022 16:11:36 +0000 Subject: [PATCH 080/460] hw/dma/xlnx_csu_dma: Support starting a read transfer through a class method An option on real hardware when embedding a DMA engine into a peripheral is to make the peripheral control the engine through a custom DMA control (hardware) interface between the two. Software drivers in this scenario configure and trigger DMA operations through the controlling peripheral's register API (for example, writing a specific bit in a register could propagate down to a transfer start signal on the DMA control interface). At the same time the status, results and interrupts for the transfer might still be intended to be read and caught through the DMA engine's register API (and signals). This patch adds a class 'read' method for allowing to start read transfers from peripherals embedding and controlling the Xilinx CSU DMA engine as in above scenario. Signed-off-by: Francisco Iglesias Reviewed-by: Luc Michel Message-id: 20220121161141.14389-6-francisco.iglesias@xilinx.com Signed-off-by: Peter Maydell --- hw/dma/xlnx_csu_dma.c | 17 +++++++++++++++++ include/hw/dma/xlnx_csu_dma.h | 19 +++++++++++++++++-- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/hw/dma/xlnx_csu_dma.c b/hw/dma/xlnx_csu_dma.c index 896bb3574d..095f954476 100644 --- a/hw/dma/xlnx_csu_dma.c +++ b/hw/dma/xlnx_csu_dma.c @@ -472,6 +472,20 @@ static uint64_t addr_msb_pre_write(RegisterInfo *reg, uint64_t val) return val & R_ADDR_MSB_ADDR_MSB_MASK; } +static MemTxResult xlnx_csu_dma_class_read(XlnxCSUDMA *s, hwaddr addr, + uint32_t len) +{ + RegisterInfo *reg = &s->regs_info[R_SIZE]; + uint64_t we = MAKE_64BIT_MASK(0, 4 * 8); + + s->regs[R_ADDR] = addr; + s->regs[R_ADDR_MSB] = (uint64_t)addr >> 32; + + register_write(reg, len, we, object_get_typename(OBJECT(s)), false); + + return (s->regs[R_SIZE] == 0) ? MEMTX_OK : MEMTX_ERROR; +} + static const RegisterAccessInfo *xlnx_csu_dma_regs_info[] = { #define DMACH_REGINFO(NAME, snd) \ (const RegisterAccessInfo []) { \ @@ -696,6 +710,7 @@ static void xlnx_csu_dma_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); StreamSinkClass *ssc = STREAM_SINK_CLASS(klass); + XlnxCSUDMAClass *xcdc = XLNX_CSU_DMA_CLASS(klass); dc->reset = xlnx_csu_dma_reset; dc->realize = xlnx_csu_dma_realize; @@ -704,6 +719,8 @@ static void xlnx_csu_dma_class_init(ObjectClass *klass, void *data) ssc->push = xlnx_csu_dma_stream_push; ssc->can_push = xlnx_csu_dma_stream_can_push; + + xcdc->read = xlnx_csu_dma_class_read; } static void xlnx_csu_dma_init(Object *obj) diff --git a/include/hw/dma/xlnx_csu_dma.h b/include/hw/dma/xlnx_csu_dma.h index 28806628b1..922ab80eb6 100644 --- a/include/hw/dma/xlnx_csu_dma.h +++ b/include/hw/dma/xlnx_csu_dma.h @@ -51,7 +51,22 @@ typedef struct XlnxCSUDMA { RegisterInfo regs_info[XLNX_CSU_DMA_R_MAX]; } XlnxCSUDMA; -#define XLNX_CSU_DMA(obj) \ - OBJECT_CHECK(XlnxCSUDMA, (obj), TYPE_XLNX_CSU_DMA) +OBJECT_DECLARE_TYPE(XlnxCSUDMA, XlnxCSUDMAClass, XLNX_CSU_DMA) + +struct XlnxCSUDMAClass { + SysBusDeviceClass parent_class; + + /* + * read: Start a read transfer on a Xilinx CSU DMA engine + * + * @s: the Xilinx CSU DMA engine to start the transfer on + * @addr: the address to read + * @len: the number of bytes to read at 'addr' + * + * @return a MemTxResult indicating whether the operation succeeded ('len' + * bytes were read) or failed. + */ + MemTxResult (*read)(XlnxCSUDMA *s, hwaddr addr, uint32_t len); +}; #endif From cbb45ff038cdbfcc8af158191405e2597f28c562 Mon Sep 17 00:00:00 2001 From: Francisco Iglesias Date: Fri, 21 Jan 2022 16:11:37 +0000 Subject: [PATCH 081/460] hw/ssi: Add a model of Xilinx Versal's OSPI flash memory controller Add a model of Xilinx Versal's OSPI flash memory controller. Signed-off-by: Francisco Iglesias Reviewed-by: Luc Michel Message-id: 20220121161141.14389-7-francisco.iglesias@xilinx.com [PMM: fixed indent] Signed-off-by: Peter Maydell --- hw/ssi/meson.build | 1 + hw/ssi/xlnx-versal-ospi.c | 1853 +++++++++++++++++++++++++++++ include/hw/ssi/xlnx-versal-ospi.h | 111 ++ 3 files changed, 1965 insertions(+) create mode 100644 hw/ssi/xlnx-versal-ospi.c create mode 100644 include/hw/ssi/xlnx-versal-ospi.h diff --git a/hw/ssi/meson.build b/hw/ssi/meson.build index 3d6bc82ab1..0ded9cd092 100644 --- a/hw/ssi/meson.build +++ b/hw/ssi/meson.build @@ -7,5 +7,6 @@ softmmu_ss.add(when: 'CONFIG_SSI', if_true: files('ssi.c')) softmmu_ss.add(when: 'CONFIG_STM32F2XX_SPI', if_true: files('stm32f2xx_spi.c')) softmmu_ss.add(when: 'CONFIG_XILINX_SPI', if_true: files('xilinx_spi.c')) softmmu_ss.add(when: 'CONFIG_XILINX_SPIPS', if_true: files('xilinx_spips.c')) +softmmu_ss.add(when: 'CONFIG_XLNX_VERSAL', if_true: files('xlnx-versal-ospi.c')) softmmu_ss.add(when: 'CONFIG_IMX', if_true: files('imx_spi.c')) softmmu_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_spi.c')) diff --git a/hw/ssi/xlnx-versal-ospi.c b/hw/ssi/xlnx-versal-ospi.c new file mode 100644 index 0000000000..7ecd148fdf --- /dev/null +++ b/hw/ssi/xlnx-versal-ospi.c @@ -0,0 +1,1853 @@ +/* + * QEMU model of Xilinx Versal's OSPI controller. + * + * Copyright (c) 2021 Xilinx Inc. + * Written by Francisco 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 "hw/sysbus.h" +#include "migration/vmstate.h" +#include "hw/qdev-properties.h" +#include "qemu/bitops.h" +#include "qemu/log.h" +#include "hw/irq.h" +#include "hw/ssi/xlnx-versal-ospi.h" + +#ifndef XILINX_VERSAL_OSPI_ERR_DEBUG +#define XILINX_VERSAL_OSPI_ERR_DEBUG 0 +#endif + +REG32(CONFIG_REG, 0x0) + FIELD(CONFIG_REG, IDLE_FLD, 31, 1) + FIELD(CONFIG_REG, DUAL_BYTE_OPCODE_EN_FLD, 30, 1) + FIELD(CONFIG_REG, CRC_ENABLE_FLD, 29, 1) + FIELD(CONFIG_REG, CONFIG_RESV2_FLD, 26, 3) + FIELD(CONFIG_REG, PIPELINE_PHY_FLD, 25, 1) + FIELD(CONFIG_REG, ENABLE_DTR_PROTOCOL_FLD, 24, 1) + FIELD(CONFIG_REG, ENABLE_AHB_DECODER_FLD, 23, 1) + FIELD(CONFIG_REG, MSTR_BAUD_DIV_FLD, 19, 4) + FIELD(CONFIG_REG, ENTER_XIP_MODE_IMM_FLD, 18, 1) + FIELD(CONFIG_REG, ENTER_XIP_MODE_FLD, 17, 1) + FIELD(CONFIG_REG, ENB_AHB_ADDR_REMAP_FLD, 16, 1) + FIELD(CONFIG_REG, ENB_DMA_IF_FLD, 15, 1) + FIELD(CONFIG_REG, WR_PROT_FLASH_FLD, 14, 1) + FIELD(CONFIG_REG, PERIPH_CS_LINES_FLD, 10, 4) + FIELD(CONFIG_REG, PERIPH_SEL_DEC_FLD, 9, 1) + FIELD(CONFIG_REG, ENB_LEGACY_IP_MODE_FLD, 8, 1) + FIELD(CONFIG_REG, ENB_DIR_ACC_CTLR_FLD, 7, 1) + FIELD(CONFIG_REG, RESET_CFG_FLD, 6, 1) + FIELD(CONFIG_REG, RESET_PIN_FLD, 5, 1) + FIELD(CONFIG_REG, HOLD_PIN_FLD, 4, 1) + FIELD(CONFIG_REG, PHY_MODE_ENABLE_FLD, 3, 1) + FIELD(CONFIG_REG, SEL_CLK_PHASE_FLD, 2, 1) + FIELD(CONFIG_REG, SEL_CLK_POL_FLD, 1, 1) + FIELD(CONFIG_REG, ENB_SPI_FLD, 0, 1) +REG32(DEV_INSTR_RD_CONFIG_REG, 0x4) + FIELD(DEV_INSTR_RD_CONFIG_REG, RD_INSTR_RESV5_FLD, 29, 3) + FIELD(DEV_INSTR_RD_CONFIG_REG, DUMMY_RD_CLK_CYCLES_FLD, 24, 5) + FIELD(DEV_INSTR_RD_CONFIG_REG, RD_INSTR_RESV4_FLD, 21, 3) + FIELD(DEV_INSTR_RD_CONFIG_REG, MODE_BIT_ENABLE_FLD, 20, 1) + FIELD(DEV_INSTR_RD_CONFIG_REG, RD_INSTR_RESV3_FLD, 18, 2) + FIELD(DEV_INSTR_RD_CONFIG_REG, DATA_XFER_TYPE_EXT_MODE_FLD, 16, 2) + FIELD(DEV_INSTR_RD_CONFIG_REG, RD_INSTR_RESV2_FLD, 14, 2) + FIELD(DEV_INSTR_RD_CONFIG_REG, ADDR_XFER_TYPE_STD_MODE_FLD, 12, 2) + FIELD(DEV_INSTR_RD_CONFIG_REG, PRED_DIS_FLD, 11, 1) + FIELD(DEV_INSTR_RD_CONFIG_REG, DDR_EN_FLD, 10, 1) + FIELD(DEV_INSTR_RD_CONFIG_REG, INSTR_TYPE_FLD, 8, 2) + FIELD(DEV_INSTR_RD_CONFIG_REG, RD_OPCODE_NON_XIP_FLD, 0, 8) +REG32(DEV_INSTR_WR_CONFIG_REG, 0x8) + FIELD(DEV_INSTR_WR_CONFIG_REG, WR_INSTR_RESV4_FLD, 29, 3) + FIELD(DEV_INSTR_WR_CONFIG_REG, DUMMY_WR_CLK_CYCLES_FLD, 24, 5) + FIELD(DEV_INSTR_WR_CONFIG_REG, WR_INSTR_RESV3_FLD, 18, 6) + FIELD(DEV_INSTR_WR_CONFIG_REG, DATA_XFER_TYPE_EXT_MODE_FLD, 16, 2) + FIELD(DEV_INSTR_WR_CONFIG_REG, WR_INSTR_RESV2_FLD, 14, 2) + FIELD(DEV_INSTR_WR_CONFIG_REG, ADDR_XFER_TYPE_STD_MODE_FLD, 12, 2) + FIELD(DEV_INSTR_WR_CONFIG_REG, WR_INSTR_RESV1_FLD, 9, 3) + FIELD(DEV_INSTR_WR_CONFIG_REG, WEL_DIS_FLD, 8, 1) + FIELD(DEV_INSTR_WR_CONFIG_REG, WR_OPCODE_FLD, 0, 8) +REG32(DEV_DELAY_REG, 0xc) + FIELD(DEV_DELAY_REG, D_NSS_FLD, 24, 8) + FIELD(DEV_DELAY_REG, D_BTWN_FLD, 16, 8) + FIELD(DEV_DELAY_REG, D_AFTER_FLD, 8, 8) + FIELD(DEV_DELAY_REG, D_INIT_FLD, 0, 8) +REG32(RD_DATA_CAPTURE_REG, 0x10) + FIELD(RD_DATA_CAPTURE_REG, RD_DATA_RESV3_FLD, 20, 12) + FIELD(RD_DATA_CAPTURE_REG, DDR_READ_DELAY_FLD, 16, 4) + FIELD(RD_DATA_CAPTURE_REG, RD_DATA_RESV2_FLD, 9, 7) + FIELD(RD_DATA_CAPTURE_REG, DQS_ENABLE_FLD, 8, 1) + FIELD(RD_DATA_CAPTURE_REG, RD_DATA_RESV1_FLD, 6, 2) + FIELD(RD_DATA_CAPTURE_REG, SAMPLE_EDGE_SEL_FLD, 5, 1) + FIELD(RD_DATA_CAPTURE_REG, DELAY_FLD, 1, 4) + FIELD(RD_DATA_CAPTURE_REG, BYPASS_FLD, 0, 1) +REG32(DEV_SIZE_CONFIG_REG, 0x14) + FIELD(DEV_SIZE_CONFIG_REG, DEV_SIZE_RESV_FLD, 29, 3) + FIELD(DEV_SIZE_CONFIG_REG, MEM_SIZE_ON_CS3_FLD, 27, 2) + FIELD(DEV_SIZE_CONFIG_REG, MEM_SIZE_ON_CS2_FLD, 25, 2) + FIELD(DEV_SIZE_CONFIG_REG, MEM_SIZE_ON_CS1_FLD, 23, 2) + FIELD(DEV_SIZE_CONFIG_REG, MEM_SIZE_ON_CS0_FLD, 21, 2) + FIELD(DEV_SIZE_CONFIG_REG, BYTES_PER_SUBSECTOR_FLD, 16, 5) + FIELD(DEV_SIZE_CONFIG_REG, BYTES_PER_DEVICE_PAGE_FLD, 4, 12) + FIELD(DEV_SIZE_CONFIG_REG, NUM_ADDR_BYTES_FLD, 0, 4) +REG32(SRAM_PARTITION_CFG_REG, 0x18) + FIELD(SRAM_PARTITION_CFG_REG, SRAM_PARTITION_RESV_FLD, 8, 24) + FIELD(SRAM_PARTITION_CFG_REG, ADDR_FLD, 0, 8) +REG32(IND_AHB_ADDR_TRIGGER_REG, 0x1c) +REG32(DMA_PERIPH_CONFIG_REG, 0x20) + FIELD(DMA_PERIPH_CONFIG_REG, DMA_PERIPH_RESV2_FLD, 12, 20) + FIELD(DMA_PERIPH_CONFIG_REG, NUM_BURST_REQ_BYTES_FLD, 8, 4) + FIELD(DMA_PERIPH_CONFIG_REG, DMA_PERIPH_RESV1_FLD, 4, 4) + FIELD(DMA_PERIPH_CONFIG_REG, NUM_SINGLE_REQ_BYTES_FLD, 0, 4) +REG32(REMAP_ADDR_REG, 0x24) +REG32(MODE_BIT_CONFIG_REG, 0x28) + FIELD(MODE_BIT_CONFIG_REG, RX_CRC_DATA_LOW_FLD, 24, 8) + FIELD(MODE_BIT_CONFIG_REG, RX_CRC_DATA_UP_FLD, 16, 8) + FIELD(MODE_BIT_CONFIG_REG, CRC_OUT_ENABLE_FLD, 15, 1) + FIELD(MODE_BIT_CONFIG_REG, MODE_BIT_RESV1_FLD, 11, 4) + FIELD(MODE_BIT_CONFIG_REG, CHUNK_SIZE_FLD, 8, 3) + FIELD(MODE_BIT_CONFIG_REG, MODE_FLD, 0, 8) +REG32(SRAM_FILL_REG, 0x2c) + FIELD(SRAM_FILL_REG, SRAM_FILL_INDAC_WRITE_FLD, 16, 16) + FIELD(SRAM_FILL_REG, SRAM_FILL_INDAC_READ_FLD, 0, 16) +REG32(TX_THRESH_REG, 0x30) + FIELD(TX_THRESH_REG, TX_THRESH_RESV_FLD, 5, 27) + FIELD(TX_THRESH_REG, LEVEL_FLD, 0, 5) +REG32(RX_THRESH_REG, 0x34) + FIELD(RX_THRESH_REG, RX_THRESH_RESV_FLD, 5, 27) + FIELD(RX_THRESH_REG, LEVEL_FLD, 0, 5) +REG32(WRITE_COMPLETION_CTRL_REG, 0x38) + FIELD(WRITE_COMPLETION_CTRL_REG, POLL_REP_DELAY_FLD, 24, 8) + FIELD(WRITE_COMPLETION_CTRL_REG, POLL_COUNT_FLD, 16, 8) + FIELD(WRITE_COMPLETION_CTRL_REG, ENABLE_POLLING_EXP_FLD, 15, 1) + FIELD(WRITE_COMPLETION_CTRL_REG, DISABLE_POLLING_FLD, 14, 1) + FIELD(WRITE_COMPLETION_CTRL_REG, POLLING_POLARITY_FLD, 13, 1) + FIELD(WRITE_COMPLETION_CTRL_REG, WR_COMP_CTRL_RESV1_FLD, 12, 1) + FIELD(WRITE_COMPLETION_CTRL_REG, POLLING_ADDR_EN_FLD, 11, 1) + FIELD(WRITE_COMPLETION_CTRL_REG, POLLING_BIT_INDEX_FLD, 8, 3) + FIELD(WRITE_COMPLETION_CTRL_REG, OPCODE_FLD, 0, 8) +REG32(NO_OF_POLLS_BEF_EXP_REG, 0x3c) +REG32(IRQ_STATUS_REG, 0x40) + FIELD(IRQ_STATUS_REG, IRQ_STAT_RESV_FLD, 20, 12) + FIELD(IRQ_STATUS_REG, ECC_FAIL_FLD, 19, 1) + FIELD(IRQ_STATUS_REG, TX_CRC_CHUNK_BRK_FLD, 18, 1) + FIELD(IRQ_STATUS_REG, RX_CRC_DATA_VAL_FLD, 17, 1) + FIELD(IRQ_STATUS_REG, RX_CRC_DATA_ERR_FLD, 16, 1) + FIELD(IRQ_STATUS_REG, IRQ_STAT_RESV1_FLD, 15, 1) + FIELD(IRQ_STATUS_REG, STIG_REQ_INT_FLD, 14, 1) + FIELD(IRQ_STATUS_REG, POLL_EXP_INT_FLD, 13, 1) + FIELD(IRQ_STATUS_REG, INDRD_SRAM_FULL_FLD, 12, 1) + FIELD(IRQ_STATUS_REG, RX_FIFO_FULL_FLD, 11, 1) + FIELD(IRQ_STATUS_REG, RX_FIFO_NOT_EMPTY_FLD, 10, 1) + FIELD(IRQ_STATUS_REG, TX_FIFO_FULL_FLD, 9, 1) + FIELD(IRQ_STATUS_REG, TX_FIFO_NOT_FULL_FLD, 8, 1) + FIELD(IRQ_STATUS_REG, RECV_OVERFLOW_FLD, 7, 1) + FIELD(IRQ_STATUS_REG, INDIRECT_XFER_LEVEL_BREACH_FLD, 6, 1) + FIELD(IRQ_STATUS_REG, ILLEGAL_ACCESS_DET_FLD, 5, 1) + FIELD(IRQ_STATUS_REG, PROT_WR_ATTEMPT_FLD, 4, 1) + FIELD(IRQ_STATUS_REG, INDIRECT_TRANSFER_REJECT_FLD, 3, 1) + FIELD(IRQ_STATUS_REG, INDIRECT_OP_DONE_FLD, 2, 1) + FIELD(IRQ_STATUS_REG, UNDERFLOW_DET_FLD, 1, 1) + FIELD(IRQ_STATUS_REG, MODE_M_FAIL_FLD, 0, 1) +REG32(IRQ_MASK_REG, 0x44) + FIELD(IRQ_MASK_REG, IRQ_MASK_RESV_FLD, 20, 12) + FIELD(IRQ_MASK_REG, ECC_FAIL_MASK_FLD, 19, 1) + FIELD(IRQ_MASK_REG, TX_CRC_CHUNK_BRK_MASK_FLD, 18, 1) + FIELD(IRQ_MASK_REG, RX_CRC_DATA_VAL_MASK_FLD, 17, 1) + FIELD(IRQ_MASK_REG, RX_CRC_DATA_ERR_MASK_FLD, 16, 1) + FIELD(IRQ_MASK_REG, IRQ_MASK_RESV1_FLD, 15, 1) + FIELD(IRQ_MASK_REG, STIG_REQ_MASK_FLD, 14, 1) + FIELD(IRQ_MASK_REG, POLL_EXP_INT_MASK_FLD, 13, 1) + FIELD(IRQ_MASK_REG, INDRD_SRAM_FULL_MASK_FLD, 12, 1) + FIELD(IRQ_MASK_REG, RX_FIFO_FULL_MASK_FLD, 11, 1) + FIELD(IRQ_MASK_REG, RX_FIFO_NOT_EMPTY_MASK_FLD, 10, 1) + FIELD(IRQ_MASK_REG, TX_FIFO_FULL_MASK_FLD, 9, 1) + FIELD(IRQ_MASK_REG, TX_FIFO_NOT_FULL_MASK_FLD, 8, 1) + FIELD(IRQ_MASK_REG, RECV_OVERFLOW_MASK_FLD, 7, 1) + FIELD(IRQ_MASK_REG, INDIRECT_XFER_LEVEL_BREACH_MASK_FLD, 6, 1) + FIELD(IRQ_MASK_REG, ILLEGAL_ACCESS_DET_MASK_FLD, 5, 1) + FIELD(IRQ_MASK_REG, PROT_WR_ATTEMPT_MASK_FLD, 4, 1) + FIELD(IRQ_MASK_REG, INDIRECT_TRANSFER_REJECT_MASK_FLD, 3, 1) + FIELD(IRQ_MASK_REG, INDIRECT_OP_DONE_MASK_FLD, 2, 1) + FIELD(IRQ_MASK_REG, UNDERFLOW_DET_MASK_FLD, 1, 1) + FIELD(IRQ_MASK_REG, MODE_M_FAIL_MASK_FLD, 0, 1) +REG32(LOWER_WR_PROT_REG, 0x50) +REG32(UPPER_WR_PROT_REG, 0x54) +REG32(WR_PROT_CTRL_REG, 0x58) + FIELD(WR_PROT_CTRL_REG, WR_PROT_CTRL_RESV_FLD, 2, 30) + FIELD(WR_PROT_CTRL_REG, ENB_FLD, 1, 1) + FIELD(WR_PROT_CTRL_REG, INV_FLD, 0, 1) +REG32(INDIRECT_READ_XFER_CTRL_REG, 0x60) + FIELD(INDIRECT_READ_XFER_CTRL_REG, INDIR_RD_XFER_RESV_FLD, 8, 24) + FIELD(INDIRECT_READ_XFER_CTRL_REG, NUM_IND_OPS_DONE_FLD, 6, 2) + FIELD(INDIRECT_READ_XFER_CTRL_REG, IND_OPS_DONE_STATUS_FLD, 5, 1) + FIELD(INDIRECT_READ_XFER_CTRL_REG, RD_QUEUED_FLD, 4, 1) + FIELD(INDIRECT_READ_XFER_CTRL_REG, SRAM_FULL_FLD, 3, 1) + FIELD(INDIRECT_READ_XFER_CTRL_REG, RD_STATUS_FLD, 2, 1) + FIELD(INDIRECT_READ_XFER_CTRL_REG, CANCEL_FLD, 1, 1) + FIELD(INDIRECT_READ_XFER_CTRL_REG, START_FLD, 0, 1) +REG32(INDIRECT_READ_XFER_WATERMARK_REG, 0x64) +REG32(INDIRECT_READ_XFER_START_REG, 0x68) +REG32(INDIRECT_READ_XFER_NUM_BYTES_REG, 0x6c) +REG32(INDIRECT_WRITE_XFER_CTRL_REG, 0x70) + FIELD(INDIRECT_WRITE_XFER_CTRL_REG, INDIR_WR_XFER_RESV2_FLD, 8, 24) + FIELD(INDIRECT_WRITE_XFER_CTRL_REG, NUM_IND_OPS_DONE_FLD, 6, 2) + FIELD(INDIRECT_WRITE_XFER_CTRL_REG, IND_OPS_DONE_STATUS_FLD, 5, 1) + FIELD(INDIRECT_WRITE_XFER_CTRL_REG, WR_QUEUED_FLD, 4, 1) + FIELD(INDIRECT_WRITE_XFER_CTRL_REG, INDIR_WR_XFER_RESV1_FLD, 3, 1) + FIELD(INDIRECT_WRITE_XFER_CTRL_REG, WR_STATUS_FLD, 2, 1) + FIELD(INDIRECT_WRITE_XFER_CTRL_REG, CANCEL_FLD, 1, 1) + FIELD(INDIRECT_WRITE_XFER_CTRL_REG, START_FLD, 0, 1) +REG32(INDIRECT_WRITE_XFER_WATERMARK_REG, 0x74) +REG32(INDIRECT_WRITE_XFER_START_REG, 0x78) +REG32(INDIRECT_WRITE_XFER_NUM_BYTES_REG, 0x7c) +REG32(INDIRECT_TRIGGER_ADDR_RANGE_REG, 0x80) + FIELD(INDIRECT_TRIGGER_ADDR_RANGE_REG, IND_RANGE_RESV1_FLD, 4, 28) + FIELD(INDIRECT_TRIGGER_ADDR_RANGE_REG, IND_RANGE_WIDTH_FLD, 0, 4) +REG32(FLASH_COMMAND_CTRL_MEM_REG, 0x8c) + FIELD(FLASH_COMMAND_CTRL_MEM_REG, FLASH_COMMAND_CTRL_MEM_RESV1_FLD, 29, 3) + FIELD(FLASH_COMMAND_CTRL_MEM_REG, MEM_BANK_ADDR_FLD, 20, 9) + FIELD(FLASH_COMMAND_CTRL_MEM_REG, FLASH_COMMAND_CTRL_MEM_RESV2_FLD, 19, 1) + FIELD(FLASH_COMMAND_CTRL_MEM_REG, NB_OF_STIG_READ_BYTES_FLD, 16, 3) + FIELD(FLASH_COMMAND_CTRL_MEM_REG, MEM_BANK_READ_DATA_FLD, 8, 8) + FIELD(FLASH_COMMAND_CTRL_MEM_REG, FLASH_COMMAND_CTRL_MEM_RESV3_FLD, 2, 6) + FIELD(FLASH_COMMAND_CTRL_MEM_REG, MEM_BANK_REQ_IN_PROGRESS_FLD, 1, 1) + FIELD(FLASH_COMMAND_CTRL_MEM_REG, TRIGGER_MEM_BANK_REQ_FLD, 0, 1) +REG32(FLASH_CMD_CTRL_REG, 0x90) + FIELD(FLASH_CMD_CTRL_REG, CMD_OPCODE_FLD, 24, 8) + FIELD(FLASH_CMD_CTRL_REG, ENB_READ_DATA_FLD, 23, 1) + FIELD(FLASH_CMD_CTRL_REG, NUM_RD_DATA_BYTES_FLD, 20, 3) + FIELD(FLASH_CMD_CTRL_REG, ENB_COMD_ADDR_FLD, 19, 1) + FIELD(FLASH_CMD_CTRL_REG, ENB_MODE_BIT_FLD, 18, 1) + FIELD(FLASH_CMD_CTRL_REG, NUM_ADDR_BYTES_FLD, 16, 2) + FIELD(FLASH_CMD_CTRL_REG, ENB_WRITE_DATA_FLD, 15, 1) + FIELD(FLASH_CMD_CTRL_REG, NUM_WR_DATA_BYTES_FLD, 12, 3) + FIELD(FLASH_CMD_CTRL_REG, NUM_DUMMY_CYCLES_FLD, 7, 5) + FIELD(FLASH_CMD_CTRL_REG, FLASH_CMD_CTRL_RESV1_FLD, 3, 4) + FIELD(FLASH_CMD_CTRL_REG, STIG_MEM_BANK_EN_FLD, 2, 1) + FIELD(FLASH_CMD_CTRL_REG, CMD_EXEC_STATUS_FLD, 1, 1) + FIELD(FLASH_CMD_CTRL_REG, CMD_EXEC_FLD, 0, 1) +REG32(FLASH_CMD_ADDR_REG, 0x94) +REG32(FLASH_RD_DATA_LOWER_REG, 0xa0) +REG32(FLASH_RD_DATA_UPPER_REG, 0xa4) +REG32(FLASH_WR_DATA_LOWER_REG, 0xa8) +REG32(FLASH_WR_DATA_UPPER_REG, 0xac) +REG32(POLLING_FLASH_STATUS_REG, 0xb0) + FIELD(POLLING_FLASH_STATUS_REG, DEVICE_STATUS_RSVD_FLD2, 21, 11) + FIELD(POLLING_FLASH_STATUS_REG, DEVICE_STATUS_NB_DUMMY, 16, 5) + FIELD(POLLING_FLASH_STATUS_REG, DEVICE_STATUS_RSVD_FLD1, 9, 7) + FIELD(POLLING_FLASH_STATUS_REG, DEVICE_STATUS_VALID_FLD, 8, 1) + FIELD(POLLING_FLASH_STATUS_REG, DEVICE_STATUS_FLD, 0, 8) +REG32(PHY_CONFIGURATION_REG, 0xb4) + FIELD(PHY_CONFIGURATION_REG, PHY_CONFIG_RESYNC_FLD, 31, 1) + FIELD(PHY_CONFIGURATION_REG, PHY_CONFIG_RESET_FLD, 30, 1) + FIELD(PHY_CONFIGURATION_REG, PHY_CONFIG_RX_DLL_BYPASS_FLD, 29, 1) + FIELD(PHY_CONFIGURATION_REG, PHY_CONFIG_RESV2_FLD, 23, 6) + FIELD(PHY_CONFIGURATION_REG, PHY_CONFIG_TX_DLL_DELAY_FLD, 16, 7) + FIELD(PHY_CONFIGURATION_REG, PHY_CONFIG_RESV1_FLD, 7, 9) + FIELD(PHY_CONFIGURATION_REG, PHY_CONFIG_RX_DLL_DELAY_FLD, 0, 7) +REG32(PHY_MASTER_CONTROL_REG, 0xb8) + FIELD(PHY_MASTER_CONTROL_REG, PHY_MASTER_CONTROL_RESV3_FLD, 25, 7) + FIELD(PHY_MASTER_CONTROL_REG, PHY_MASTER_LOCK_MODE_FLD, 24, 1) + FIELD(PHY_MASTER_CONTROL_REG, PHY_MASTER_BYPASS_MODE_FLD, 23, 1) + FIELD(PHY_MASTER_CONTROL_REG, PHY_MASTER_PHASE_DETECT_SELECTOR_FLD, 20, 3) + FIELD(PHY_MASTER_CONTROL_REG, PHY_MASTER_CONTROL_RESV2_FLD, 19, 1) + FIELD(PHY_MASTER_CONTROL_REG, PHY_MASTER_NB_INDICATIONS_FLD, 16, 3) + FIELD(PHY_MASTER_CONTROL_REG, PHY_MASTER_CONTROL_RESV1_FLD, 7, 9) + FIELD(PHY_MASTER_CONTROL_REG, PHY_MASTER_INITIAL_DELAY_FLD, 0, 7) +REG32(DLL_OBSERVABLE_LOWER_REG, 0xbc) + FIELD(DLL_OBSERVABLE_LOWER_REG, + DLL_OBSERVABLE_LOWER_DLL_LOCK_INC_FLD, 24, 8) + FIELD(DLL_OBSERVABLE_LOWER_REG, + DLL_OBSERVABLE_LOWER_DLL_LOCK_DEC_FLD, 16, 8) + FIELD(DLL_OBSERVABLE_LOWER_REG, + DLL_OBSERVABLE_LOWER_LOOPBACK_LOCK_FLD, 15, 1) + FIELD(DLL_OBSERVABLE_LOWER_REG, + DLL_OBSERVABLE_LOWER_LOCK_VALUE_FLD, 8, 7) + FIELD(DLL_OBSERVABLE_LOWER_REG, + DLL_OBSERVABLE_LOWER_UNLOCK_COUNTER_FLD, 3, 5) + FIELD(DLL_OBSERVABLE_LOWER_REG, + DLL_OBSERVABLE_LOWER_LOCK_MODE_FLD, 1, 2) + FIELD(DLL_OBSERVABLE_LOWER_REG, + DLL_OBSERVABLE_LOWER_DLL_LOCK_FLD, 0, 1) +REG32(DLL_OBSERVABLE_UPPER_REG, 0xc0) + FIELD(DLL_OBSERVABLE_UPPER_REG, + DLL_OBSERVABLE_UPPER_RESV2_FLD, 23, 9) + FIELD(DLL_OBSERVABLE_UPPER_REG, + DLL_OBSERVABLE_UPPER_TX_DECODER_OUTPUT_FLD, 16, 7) + FIELD(DLL_OBSERVABLE_UPPER_REG, + DLL_OBSERVABLE_UPPER_RESV1_FLD, 7, 9) + FIELD(DLL_OBSERVABLE_UPPER_REG, + DLL_OBSERVABLE__UPPER_RX_DECODER_OUTPUT_FLD, 0, 7) +REG32(OPCODE_EXT_LOWER_REG, 0xe0) + FIELD(OPCODE_EXT_LOWER_REG, EXT_READ_OPCODE_FLD, 24, 8) + FIELD(OPCODE_EXT_LOWER_REG, EXT_WRITE_OPCODE_FLD, 16, 8) + FIELD(OPCODE_EXT_LOWER_REG, EXT_POLL_OPCODE_FLD, 8, 8) + FIELD(OPCODE_EXT_LOWER_REG, EXT_STIG_OPCODE_FLD, 0, 8) +REG32(OPCODE_EXT_UPPER_REG, 0xe4) + FIELD(OPCODE_EXT_UPPER_REG, WEL_OPCODE_FLD, 24, 8) + FIELD(OPCODE_EXT_UPPER_REG, EXT_WEL_OPCODE_FLD, 16, 8) + FIELD(OPCODE_EXT_UPPER_REG, OPCODE_EXT_UPPER_RESV1_FLD, 0, 16) +REG32(MODULE_ID_REG, 0xfc) + FIELD(MODULE_ID_REG, FIX_PATCH_FLD, 24, 8) + FIELD(MODULE_ID_REG, MODULE_ID_FLD, 8, 16) + FIELD(MODULE_ID_REG, MODULE_ID_RESV_FLD, 2, 6) + FIELD(MODULE_ID_REG, CONF_FLD, 0, 2) + +#define RXFF_SZ 1024 +#define TXFF_SZ 1024 + +#define MAX_RX_DEC_OUT 8 + +#define SZ_512MBIT (512 * 1024 * 1024) +#define SZ_1GBIT (1024 * 1024 * 1024) +#define SZ_2GBIT (2ULL * SZ_1GBIT) +#define SZ_4GBIT (4ULL * SZ_1GBIT) + +#define IS_IND_DMA_START(op) (op->done_bytes == 0) +/* + * Bit field size of R_INDIRECT_WRITE_XFER_CTRL_REG_NUM_IND_OPS_DONE_FLD + * is 2 bits, which can record max of 3 indac operations. + */ +#define IND_OPS_DONE_MAX 3 + +typedef enum { + WREN = 0x6, +} FlashCMD; + +static unsigned int ospi_stig_addr_len(XlnxVersalOspi *s) +{ + /* Num address bytes is NUM_ADDR_BYTES_FLD + 1 */ + return ARRAY_FIELD_EX32(s->regs, + FLASH_CMD_CTRL_REG, NUM_ADDR_BYTES_FLD) + 1; +} + +static unsigned int ospi_stig_wr_data_len(XlnxVersalOspi *s) +{ + /* Num write data bytes is NUM_WR_DATA_BYTES_FLD + 1 */ + return ARRAY_FIELD_EX32(s->regs, + FLASH_CMD_CTRL_REG, NUM_WR_DATA_BYTES_FLD) + 1; +} + +static unsigned int ospi_stig_rd_data_len(XlnxVersalOspi *s) +{ + /* Num read data bytes is NUM_RD_DATA_BYTES_FLD + 1 */ + return ARRAY_FIELD_EX32(s->regs, + FLASH_CMD_CTRL_REG, NUM_RD_DATA_BYTES_FLD) + 1; +} + +/* + * Status bits in R_IRQ_STATUS_REG are set when the event occurs and the + * interrupt is enabled in the mask register ([1] Section 2.3.17) + */ +static void set_irq(XlnxVersalOspi *s, uint32_t set_mask) +{ + s->regs[R_IRQ_STATUS_REG] |= s->regs[R_IRQ_MASK_REG] & set_mask; +} + +static void ospi_update_irq_line(XlnxVersalOspi *s) +{ + qemu_set_irq(s->irq, !!(s->regs[R_IRQ_STATUS_REG] & + s->regs[R_IRQ_MASK_REG])); +} + +static uint8_t ospi_get_wr_opcode(XlnxVersalOspi *s) +{ + return ARRAY_FIELD_EX32(s->regs, + DEV_INSTR_WR_CONFIG_REG, WR_OPCODE_FLD); +} + +static uint8_t ospi_get_rd_opcode(XlnxVersalOspi *s) +{ + return ARRAY_FIELD_EX32(s->regs, + DEV_INSTR_RD_CONFIG_REG, RD_OPCODE_NON_XIP_FLD); +} + +static uint32_t ospi_get_num_addr_bytes(XlnxVersalOspi *s) +{ + /* Num address bytes is NUM_ADDR_BYTES_FLD + 1 */ + return ARRAY_FIELD_EX32(s->regs, + DEV_SIZE_CONFIG_REG, NUM_ADDR_BYTES_FLD) + 1; +} + +static void ospi_stig_membank_req(XlnxVersalOspi *s) +{ + int idx = ARRAY_FIELD_EX32(s->regs, + FLASH_COMMAND_CTRL_MEM_REG, MEM_BANK_ADDR_FLD); + + ARRAY_FIELD_DP32(s->regs, FLASH_COMMAND_CTRL_MEM_REG, + MEM_BANK_READ_DATA_FLD, s->stig_membank[idx]); +} + +static int ospi_stig_membank_rd_bytes(XlnxVersalOspi *s) +{ + int rd_data_fld = ARRAY_FIELD_EX32(s->regs, FLASH_COMMAND_CTRL_MEM_REG, + NB_OF_STIG_READ_BYTES_FLD); + static const int sizes[6] = { 16, 32, 64, 128, 256, 512 }; + return (rd_data_fld < 6) ? sizes[rd_data_fld] : 0; +} + +static uint32_t ospi_get_page_sz(XlnxVersalOspi *s) +{ + return ARRAY_FIELD_EX32(s->regs, + DEV_SIZE_CONFIG_REG, BYTES_PER_DEVICE_PAGE_FLD); +} + +static bool ospi_ind_rd_watermark_enabled(XlnxVersalOspi *s) +{ + return s->regs[R_INDIRECT_READ_XFER_WATERMARK_REG]; +} + +static void ind_op_advance(IndOp *op, unsigned int len) +{ + op->done_bytes += len; + assert(op->done_bytes <= op->num_bytes); + if (op->done_bytes == op->num_bytes) { + op->completed = true; + } +} + +static uint32_t ind_op_next_byte(IndOp *op) +{ + return op->flash_addr + op->done_bytes; +} + +static uint32_t ind_op_end_byte(IndOp *op) +{ + return op->flash_addr + op->num_bytes; +} + +static void ospi_ind_op_next(IndOp *op) +{ + op[0] = op[1]; + op[1].completed = true; +} + +static void ind_op_setup(IndOp *op, uint32_t flash_addr, uint32_t num_bytes) +{ + if (num_bytes & 0x3) { + qemu_log_mask(LOG_GUEST_ERROR, + "OSPI indirect op num bytes not word aligned\n"); + } + op->flash_addr = flash_addr; + op->num_bytes = num_bytes; + op->done_bytes = 0; + op->completed = false; +} + +static bool ospi_ind_op_completed(IndOp *op) +{ + return op->completed; +} + +static bool ospi_ind_op_all_completed(XlnxVersalOspi *s) +{ + return s->rd_ind_op[0].completed && s->wr_ind_op[0].completed; +} + +static void ospi_ind_op_cancel(IndOp *op) +{ + op[0].completed = true; + op[1].completed = true; +} + +static bool ospi_ind_op_add(IndOp *op, Fifo8 *fifo, + uint32_t flash_addr, uint32_t num_bytes) +{ + /* Check if first indirect op has been completed */ + if (op->completed) { + fifo8_reset(fifo); + ind_op_setup(op, flash_addr, num_bytes); + return false; + } + + /* Check if second indirect op has been completed */ + op++; + if (op->completed) { + ind_op_setup(op, flash_addr, num_bytes); + return false; + } + return true; +} + +static void ospi_ind_op_queue_up_rd(XlnxVersalOspi *s) +{ + uint32_t num_bytes = s->regs[R_INDIRECT_READ_XFER_NUM_BYTES_REG]; + uint32_t flash_addr = s->regs[R_INDIRECT_READ_XFER_START_REG]; + bool failed; + + failed = ospi_ind_op_add(s->rd_ind_op, &s->rx_sram, flash_addr, num_bytes); + /* If two already queued set rd reject interrupt */ + if (failed) { + set_irq(s, R_IRQ_STATUS_REG_INDIRECT_TRANSFER_REJECT_FLD_MASK); + } +} + +static void ospi_ind_op_queue_up_wr(XlnxVersalOspi *s) +{ + uint32_t num_bytes = s->regs[R_INDIRECT_WRITE_XFER_NUM_BYTES_REG]; + uint32_t flash_addr = s->regs[R_INDIRECT_WRITE_XFER_START_REG]; + bool failed; + + failed = ospi_ind_op_add(s->wr_ind_op, &s->tx_sram, flash_addr, num_bytes); + /* If two already queued set rd reject interrupt */ + if (failed) { + set_irq(s, R_IRQ_STATUS_REG_INDIRECT_TRANSFER_REJECT_FLD_MASK); + } +} + +static uint64_t flash_sz(XlnxVersalOspi *s, unsigned int cs) +{ + /* Flash sizes in MB */ + static const uint64_t sizes[4] = { SZ_512MBIT / 8, SZ_1GBIT / 8, + SZ_2GBIT / 8, SZ_4GBIT / 8 }; + uint32_t v = s->regs[R_DEV_SIZE_CONFIG_REG]; + + v >>= cs * R_DEV_SIZE_CONFIG_REG_MEM_SIZE_ON_CS0_FLD_LENGTH; + return sizes[FIELD_EX32(v, DEV_SIZE_CONFIG_REG, MEM_SIZE_ON_CS0_FLD)]; +} + +static unsigned int ospi_get_block_sz(XlnxVersalOspi *s) +{ + unsigned int block_fld = ARRAY_FIELD_EX32(s->regs, + DEV_SIZE_CONFIG_REG, + BYTES_PER_SUBSECTOR_FLD); + return 1 << block_fld; +} + +static unsigned int flash_blocks(XlnxVersalOspi *s, unsigned int cs) +{ + unsigned int b_sz = ospi_get_block_sz(s); + unsigned int f_sz = flash_sz(s, cs); + + return f_sz / b_sz; +} + +static int ospi_ahb_decoder_cs(XlnxVersalOspi *s, hwaddr addr) +{ + uint64_t end_addr = 0; + int cs; + + for (cs = 0; cs < s->num_cs; cs++) { + end_addr += flash_sz(s, cs); + if (addr < end_addr) { + break; + } + } + + if (cs == s->num_cs) { + /* Address is out of range */ + qemu_log_mask(LOG_GUEST_ERROR, + "OSPI flash address does not fit in configuration\n"); + return -1; + } + return cs; +} + +static void ospi_ahb_decoder_enable_cs(XlnxVersalOspi *s, hwaddr addr) +{ + int cs = ospi_ahb_decoder_cs(s, addr); + + if (cs >= 0) { + for (int i = 0; i < s->num_cs; i++) { + qemu_set_irq(s->cs_lines[i], cs != i); + } + } +} + +static unsigned int single_cs(XlnxVersalOspi *s) +{ + unsigned int field = ARRAY_FIELD_EX32(s->regs, + CONFIG_REG, PERIPH_CS_LINES_FLD); + + /* + * Below one liner is a trick that finds the rightmost zero and makes sure + * all other bits are turned to 1. It is a variant of the 'Isolate the + * rightmost 0-bit' trick found below at the time of writing: + * + * https://emre.me/computer-science/bit-manipulation-tricks/ + * + * 4'bXXX0 -> 4'b1110 + * 4'bXX01 -> 4'b1101 + * 4'bX011 -> 4'b1011 + * 4'b0111 -> 4'b0111 + * 4'b1111 -> 4'b1111 + */ + return (field | ~(field + 1)) & 0xf; +} + +static void ospi_update_cs_lines(XlnxVersalOspi *s) +{ + unsigned int all_cs; + int i; + + if (ARRAY_FIELD_EX32(s->regs, CONFIG_REG, PERIPH_SEL_DEC_FLD)) { + all_cs = ARRAY_FIELD_EX32(s->regs, CONFIG_REG, PERIPH_CS_LINES_FLD); + } else { + all_cs = single_cs(s); + } + + for (i = 0; i < s->num_cs; i++) { + bool cs = (all_cs >> i) & 1; + + qemu_set_irq(s->cs_lines[i], cs); + } +} + +static void ospi_dac_cs(XlnxVersalOspi *s, hwaddr addr) +{ + if (ARRAY_FIELD_EX32(s->regs, CONFIG_REG, ENABLE_AHB_DECODER_FLD)) { + ospi_ahb_decoder_enable_cs(s, addr); + } else { + ospi_update_cs_lines(s); + } +} + +static void ospi_disable_cs(XlnxVersalOspi *s) +{ + int i; + + for (i = 0; i < s->num_cs; i++) { + qemu_set_irq(s->cs_lines[i], 1); + } +} + +static void ospi_flush_txfifo(XlnxVersalOspi *s) +{ + while (!fifo8_is_empty(&s->tx_fifo)) { + uint32_t tx_rx = fifo8_pop(&s->tx_fifo); + + tx_rx = ssi_transfer(s->spi, tx_rx); + fifo8_push(&s->rx_fifo, tx_rx); + } +} + +static void ospi_tx_fifo_push_address_raw(XlnxVersalOspi *s, + uint32_t flash_addr, + unsigned int addr_bytes) +{ + /* Push write address */ + if (addr_bytes == 4) { + fifo8_push(&s->tx_fifo, flash_addr >> 24); + } + if (addr_bytes >= 3) { + fifo8_push(&s->tx_fifo, flash_addr >> 16); + } + if (addr_bytes >= 2) { + fifo8_push(&s->tx_fifo, flash_addr >> 8); + } + fifo8_push(&s->tx_fifo, flash_addr); +} + +static void ospi_tx_fifo_push_address(XlnxVersalOspi *s, uint32_t flash_addr) +{ + /* Push write address */ + int addr_bytes = ospi_get_num_addr_bytes(s); + + ospi_tx_fifo_push_address_raw(s, flash_addr, addr_bytes); +} + +static void ospi_tx_fifo_push_stig_addr(XlnxVersalOspi *s) +{ + uint32_t flash_addr = s->regs[R_FLASH_CMD_ADDR_REG]; + unsigned int addr_bytes = ospi_stig_addr_len(s); + + ospi_tx_fifo_push_address_raw(s, flash_addr, addr_bytes); +} + +static void ospi_tx_fifo_push_rd_op_addr(XlnxVersalOspi *s, uint32_t flash_addr) +{ + uint8_t inst_code = ospi_get_rd_opcode(s); + + fifo8_reset(&s->tx_fifo); + + /* Push read opcode */ + fifo8_push(&s->tx_fifo, inst_code); + + /* Push read address */ + ospi_tx_fifo_push_address(s, flash_addr); +} + +static void ospi_tx_fifo_push_stig_wr_data(XlnxVersalOspi *s) +{ + uint64_t data = s->regs[R_FLASH_WR_DATA_LOWER_REG]; + int wr_data_len = ospi_stig_wr_data_len(s); + int i; + + data |= (uint64_t) s->regs[R_FLASH_WR_DATA_UPPER_REG] << 32; + for (i = 0; i < wr_data_len; i++) { + int shift = i * 8; + fifo8_push(&s->tx_fifo, data >> shift); + } +} + +static void ospi_tx_fifo_push_stig_rd_data(XlnxVersalOspi *s) +{ + int rd_data_len; + int i; + + if (ARRAY_FIELD_EX32(s->regs, FLASH_CMD_CTRL_REG, STIG_MEM_BANK_EN_FLD)) { + rd_data_len = ospi_stig_membank_rd_bytes(s); + } else { + rd_data_len = ospi_stig_rd_data_len(s); + } + + /* transmit second part (data) */ + for (i = 0; i < rd_data_len; ++i) { + fifo8_push(&s->tx_fifo, 0); + } +} + +static void ospi_rx_fifo_pop_stig_rd_data(XlnxVersalOspi *s) +{ + int size = ospi_stig_rd_data_len(s); + uint8_t bytes[8] = {}; + int i; + + size = MIN(fifo8_num_used(&s->rx_fifo), size); + + assert(size <= 8); + + for (i = 0; i < size; i++) { + bytes[i] = fifo8_pop(&s->rx_fifo); + } + + s->regs[R_FLASH_RD_DATA_LOWER_REG] = ldl_le_p(bytes); + s->regs[R_FLASH_RD_DATA_UPPER_REG] = ldl_le_p(bytes + 4); +} + +static void ospi_ind_read(XlnxVersalOspi *s, uint32_t flash_addr, uint32_t len) +{ + int i; + + /* Create first section of read cmd */ + ospi_tx_fifo_push_rd_op_addr(s, flash_addr); + + /* transmit first part */ + ospi_update_cs_lines(s); + ospi_flush_txfifo(s); + + fifo8_reset(&s->rx_fifo); + + /* transmit second part (data) */ + for (i = 0; i < len; ++i) { + fifo8_push(&s->tx_fifo, 0); + } + ospi_flush_txfifo(s); + + for (i = 0; i < len; ++i) { + fifo8_push(&s->rx_sram, fifo8_pop(&s->rx_fifo)); + } + + /* done */ + ospi_disable_cs(s); +} + +static unsigned int ospi_dma_burst_size(XlnxVersalOspi *s) +{ + return 1 << ARRAY_FIELD_EX32(s->regs, + DMA_PERIPH_CONFIG_REG, + NUM_BURST_REQ_BYTES_FLD); +} + +static unsigned int ospi_dma_single_size(XlnxVersalOspi *s) +{ + return 1 << ARRAY_FIELD_EX32(s->regs, + DMA_PERIPH_CONFIG_REG, + NUM_SINGLE_REQ_BYTES_FLD); +} + +static void ind_rd_inc_num_done(XlnxVersalOspi *s) +{ + unsigned int done = ARRAY_FIELD_EX32(s->regs, + INDIRECT_READ_XFER_CTRL_REG, + NUM_IND_OPS_DONE_FLD); + if (done < IND_OPS_DONE_MAX) { + done++; + } + done &= 0x3; + ARRAY_FIELD_DP32(s->regs, INDIRECT_READ_XFER_CTRL_REG, + NUM_IND_OPS_DONE_FLD, done); +} + +static void ospi_ind_rd_completed(XlnxVersalOspi *s) +{ + ARRAY_FIELD_DP32(s->regs, INDIRECT_READ_XFER_CTRL_REG, + IND_OPS_DONE_STATUS_FLD, 1); + + ind_rd_inc_num_done(s); + ospi_ind_op_next(s->rd_ind_op); + if (ospi_ind_op_all_completed(s)) { + set_irq(s, R_IRQ_STATUS_REG_INDIRECT_OP_DONE_FLD_MASK); + } +} + +static void ospi_dma_read(XlnxVersalOspi *s) +{ + IndOp *op = s->rd_ind_op; + uint32_t dma_len = op->num_bytes; + uint32_t burst_sz = ospi_dma_burst_size(s); + uint32_t single_sz = ospi_dma_single_size(s); + uint32_t ind_trig_range; + uint32_t remainder; + XlnxCSUDMAClass *xcdc = XLNX_CSU_DMA_GET_CLASS(s->dma_src); + + ind_trig_range = (1 << ARRAY_FIELD_EX32(s->regs, + INDIRECT_TRIGGER_ADDR_RANGE_REG, + IND_RANGE_WIDTH_FLD)); + remainder = dma_len % burst_sz; + remainder = remainder % single_sz; + if (burst_sz > ind_trig_range || single_sz > ind_trig_range || + remainder != 0) { + qemu_log_mask(LOG_GUEST_ERROR, + "OSPI DMA burst size / single size config error\n"); + } + + s->src_dma_inprog = true; + if (xcdc->read(s->dma_src, 0, dma_len) != MEMTX_OK) { + qemu_log_mask(LOG_GUEST_ERROR, "OSPI DMA configuration error\n"); + } + s->src_dma_inprog = false; +} + +static void ospi_do_ind_read(XlnxVersalOspi *s) +{ + IndOp *op = s->rd_ind_op; + uint32_t next_b; + uint32_t end_b; + uint32_t len; + bool start_dma = IS_IND_DMA_START(op) && !s->src_dma_inprog; + + /* Continue to read flash until we run out of space in sram */ + while (!ospi_ind_op_completed(op) && + !fifo8_is_full(&s->rx_sram)) { + /* Read reqested number of bytes, max bytes limited to size of sram */ + next_b = ind_op_next_byte(op); + end_b = next_b + fifo8_num_free(&s->rx_sram); + end_b = MIN(end_b, ind_op_end_byte(op)); + + len = end_b - next_b; + ospi_ind_read(s, next_b, len); + ind_op_advance(op, len); + + if (ospi_ind_rd_watermark_enabled(s)) { + ARRAY_FIELD_DP32(s->regs, IRQ_STATUS_REG, + INDIRECT_XFER_LEVEL_BREACH_FLD, 1); + set_irq(s, + R_IRQ_STATUS_REG_INDIRECT_XFER_LEVEL_BREACH_FLD_MASK); + } + + if (!s->src_dma_inprog && + ARRAY_FIELD_EX32(s->regs, CONFIG_REG, ENB_DMA_IF_FLD)) { + ospi_dma_read(s); + } + } + + /* Set sram full */ + if (fifo8_num_used(&s->rx_sram) == RXFF_SZ) { + ARRAY_FIELD_DP32(s->regs, + INDIRECT_READ_XFER_CTRL_REG, SRAM_FULL_FLD, 1); + set_irq(s, R_IRQ_STATUS_REG_INDRD_SRAM_FULL_FLD_MASK); + } + + /* Signal completion if done, unless inside recursion via ospi_dma_read */ + if (!ARRAY_FIELD_EX32(s->regs, CONFIG_REG, ENB_DMA_IF_FLD) || start_dma) { + if (ospi_ind_op_completed(op)) { + ospi_ind_rd_completed(s); + } + } +} + +/* Transmit write enable instruction */ +static void ospi_transmit_wel(XlnxVersalOspi *s, bool ahb_decoder_cs, + hwaddr addr) +{ + fifo8_reset(&s->tx_fifo); + fifo8_push(&s->tx_fifo, WREN); + + if (ahb_decoder_cs) { + ospi_ahb_decoder_enable_cs(s, addr); + } else { + ospi_update_cs_lines(s); + } + + ospi_flush_txfifo(s); + ospi_disable_cs(s); + + fifo8_reset(&s->rx_fifo); +} + +static void ospi_ind_write(XlnxVersalOspi *s, uint32_t flash_addr, uint32_t len) +{ + bool ahb_decoder_cs = false; + uint8_t inst_code; + int i; + + assert(fifo8_num_used(&s->tx_sram) >= len); + + if (!ARRAY_FIELD_EX32(s->regs, DEV_INSTR_WR_CONFIG_REG, WEL_DIS_FLD)) { + ospi_transmit_wel(s, ahb_decoder_cs, 0); + } + + /* reset fifos */ + fifo8_reset(&s->tx_fifo); + fifo8_reset(&s->rx_fifo); + + /* Push write opcode */ + inst_code = ospi_get_wr_opcode(s); + fifo8_push(&s->tx_fifo, inst_code); + + /* Push write address */ + ospi_tx_fifo_push_address(s, flash_addr); + + /* data */ + for (i = 0; i < len; i++) { + fifo8_push(&s->tx_fifo, fifo8_pop(&s->tx_sram)); + } + + /* transmit */ + ospi_update_cs_lines(s); + ospi_flush_txfifo(s); + + /* done */ + ospi_disable_cs(s); + fifo8_reset(&s->rx_fifo); +} + +static void ind_wr_inc_num_done(XlnxVersalOspi *s) +{ + unsigned int done = ARRAY_FIELD_EX32(s->regs, INDIRECT_WRITE_XFER_CTRL_REG, + NUM_IND_OPS_DONE_FLD); + if (done < IND_OPS_DONE_MAX) { + done++; + } + done &= 0x3; + ARRAY_FIELD_DP32(s->regs, INDIRECT_WRITE_XFER_CTRL_REG, + NUM_IND_OPS_DONE_FLD, done); +} + +static void ospi_ind_wr_completed(XlnxVersalOspi *s) +{ + ARRAY_FIELD_DP32(s->regs, INDIRECT_WRITE_XFER_CTRL_REG, + IND_OPS_DONE_STATUS_FLD, 1); + ind_wr_inc_num_done(s); + ospi_ind_op_next(s->wr_ind_op); + /* Set indirect op done interrupt if enabled */ + if (ospi_ind_op_all_completed(s)) { + set_irq(s, R_IRQ_STATUS_REG_INDIRECT_OP_DONE_FLD_MASK); + } +} + +static void ospi_do_indirect_write(XlnxVersalOspi *s) +{ + uint32_t write_watermark = s->regs[R_INDIRECT_WRITE_XFER_WATERMARK_REG]; + uint32_t pagesz = ospi_get_page_sz(s); + uint32_t page_mask = ~(pagesz - 1); + IndOp *op = s->wr_ind_op; + uint32_t next_b; + uint32_t end_b; + uint32_t len; + + /* Write out tx_fifo in maximum page sz chunks */ + while (!ospi_ind_op_completed(op) && fifo8_num_used(&s->tx_sram) > 0) { + next_b = ind_op_next_byte(op); + end_b = next_b + MIN(fifo8_num_used(&s->tx_sram), pagesz); + + /* Dont cross page boundary */ + if ((end_b & page_mask) > next_b) { + end_b &= page_mask; + } + + len = end_b - next_b; + len = MIN(len, op->num_bytes - op->done_bytes); + ospi_ind_write(s, next_b, len); + ind_op_advance(op, len); + } + + /* + * Always set indirect transfer level breached interrupt if enabled + * (write watermark > 0) since the tx_sram always will be emptied + */ + if (write_watermark > 0) { + set_irq(s, R_IRQ_STATUS_REG_INDIRECT_XFER_LEVEL_BREACH_FLD_MASK); + } + + /* Signal completions if done */ + if (ospi_ind_op_completed(op)) { + ospi_ind_wr_completed(s); + } +} + +static void ospi_stig_fill_membank(XlnxVersalOspi *s) +{ + int num_rd_bytes = ospi_stig_membank_rd_bytes(s); + int idx = num_rd_bytes - 8; /* first of last 8 */ + int i; + + for (i = 0; i < num_rd_bytes; i++) { + s->stig_membank[i] = fifo8_pop(&s->rx_fifo); + } + + g_assert((idx + 4) < ARRAY_SIZE(s->stig_membank)); + + /* Fill in lower upper regs */ + s->regs[R_FLASH_RD_DATA_LOWER_REG] = ldl_le_p(&s->stig_membank[idx]); + s->regs[R_FLASH_RD_DATA_UPPER_REG] = ldl_le_p(&s->stig_membank[idx + 4]); +} + +static void ospi_stig_cmd_exec(XlnxVersalOspi *s) +{ + uint8_t inst_code; + + /* Reset fifos */ + fifo8_reset(&s->tx_fifo); + fifo8_reset(&s->rx_fifo); + + /* Push write opcode */ + inst_code = ARRAY_FIELD_EX32(s->regs, FLASH_CMD_CTRL_REG, CMD_OPCODE_FLD); + fifo8_push(&s->tx_fifo, inst_code); + + /* Push address if enabled */ + if (ARRAY_FIELD_EX32(s->regs, FLASH_CMD_CTRL_REG, ENB_COMD_ADDR_FLD)) { + ospi_tx_fifo_push_stig_addr(s); + } + + /* Enable cs */ + ospi_update_cs_lines(s); + + /* Data */ + if (ARRAY_FIELD_EX32(s->regs, FLASH_CMD_CTRL_REG, ENB_WRITE_DATA_FLD)) { + ospi_tx_fifo_push_stig_wr_data(s); + } else if (ARRAY_FIELD_EX32(s->regs, + FLASH_CMD_CTRL_REG, ENB_READ_DATA_FLD)) { + /* transmit first part */ + ospi_flush_txfifo(s); + fifo8_reset(&s->rx_fifo); + ospi_tx_fifo_push_stig_rd_data(s); + } + + /* Transmit */ + ospi_flush_txfifo(s); + ospi_disable_cs(s); + + if (ARRAY_FIELD_EX32(s->regs, FLASH_CMD_CTRL_REG, ENB_READ_DATA_FLD)) { + if (ARRAY_FIELD_EX32(s->regs, + FLASH_CMD_CTRL_REG, STIG_MEM_BANK_EN_FLD)) { + ospi_stig_fill_membank(s); + } else { + ospi_rx_fifo_pop_stig_rd_data(s); + } + } +} + +static uint32_t ospi_block_address(XlnxVersalOspi *s, unsigned int block) +{ + unsigned int block_sz = ospi_get_block_sz(s); + unsigned int cs = 0; + uint32_t addr = 0; + + while (cs < s->num_cs && block >= flash_blocks(s, cs)) { + block -= flash_blocks(s, 0); + addr += flash_sz(s, cs); + } + addr += block * block_sz; + return addr; +} + +static uint32_t ospi_get_wr_prot_addr_low(XlnxVersalOspi *s) +{ + unsigned int block = s->regs[R_LOWER_WR_PROT_REG]; + + return ospi_block_address(s, block); +} + +static uint32_t ospi_get_wr_prot_addr_upper(XlnxVersalOspi *s) +{ + unsigned int block = s->regs[R_UPPER_WR_PROT_REG]; + + /* Get address of first block out of defined range */ + return ospi_block_address(s, block + 1); +} + +static bool ospi_is_write_protected(XlnxVersalOspi *s, hwaddr addr) +{ + uint32_t wr_prot_addr_upper = ospi_get_wr_prot_addr_upper(s); + uint32_t wr_prot_addr_low = ospi_get_wr_prot_addr_low(s); + bool in_range = false; + + if (addr >= wr_prot_addr_low && addr < wr_prot_addr_upper) { + in_range = true; + } + + if (ARRAY_FIELD_EX32(s->regs, WR_PROT_CTRL_REG, INV_FLD)) { + in_range = !in_range; + } + return in_range; +} + +static uint64_t ospi_rx_sram_read(XlnxVersalOspi *s, unsigned int size) +{ + uint8_t bytes[8] = {}; + int i; + + if (size < 4 && fifo8_num_used(&s->rx_sram) >= 4) { + qemu_log_mask(LOG_GUEST_ERROR, + "OSPI only last read of internal " + "sram is allowed to be < 32 bits\n"); + } + + size = MIN(fifo8_num_used(&s->rx_sram), size); + + assert(size <= 8); + + for (i = 0; i < size; i++) { + bytes[i] = fifo8_pop(&s->rx_sram); + } + + return ldq_le_p(bytes); +} + +static void ospi_tx_sram_write(XlnxVersalOspi *s, uint64_t value, + unsigned int size) +{ + int i; + for (i = 0; i < size && !fifo8_is_full(&s->tx_sram); i++) { + fifo8_push(&s->tx_sram, value >> 8 * i); + } +} + +static uint64_t ospi_do_dac_read(void *opaque, hwaddr addr, unsigned int size) +{ + XlnxVersalOspi *s = XILINX_VERSAL_OSPI(opaque); + uint8_t bytes[8] = {}; + int i; + + /* Create first section of read cmd */ + ospi_tx_fifo_push_rd_op_addr(s, (uint32_t) addr); + + /* Enable cs and transmit first part */ + ospi_dac_cs(s, addr); + ospi_flush_txfifo(s); + + fifo8_reset(&s->rx_fifo); + + /* transmit second part (data) */ + for (i = 0; i < size; ++i) { + fifo8_push(&s->tx_fifo, 0); + } + ospi_flush_txfifo(s); + + /* fill in result */ + size = MIN(fifo8_num_used(&s->rx_fifo), size); + + assert(size <= 8); + + for (i = 0; i < size; i++) { + bytes[i] = fifo8_pop(&s->rx_fifo); + } + + /* done */ + ospi_disable_cs(s); + + return ldq_le_p(bytes); +} + +static void ospi_do_dac_write(void *opaque, + hwaddr addr, + uint64_t value, + unsigned int size) +{ + XlnxVersalOspi *s = XILINX_VERSAL_OSPI(opaque); + bool ahb_decoder_cs = ARRAY_FIELD_EX32(s->regs, CONFIG_REG, + ENABLE_AHB_DECODER_FLD); + uint8_t inst_code; + unsigned int i; + + if (!ARRAY_FIELD_EX32(s->regs, DEV_INSTR_WR_CONFIG_REG, WEL_DIS_FLD)) { + ospi_transmit_wel(s, ahb_decoder_cs, addr); + } + + /* reset fifos */ + fifo8_reset(&s->tx_fifo); + fifo8_reset(&s->rx_fifo); + + /* Push write opcode */ + inst_code = ospi_get_wr_opcode(s); + fifo8_push(&s->tx_fifo, inst_code); + + /* Push write address */ + ospi_tx_fifo_push_address(s, addr); + + /* data */ + for (i = 0; i < size; i++) { + fifo8_push(&s->tx_fifo, value >> 8 * i); + } + + /* Enable cs and transmit */ + ospi_dac_cs(s, addr); + ospi_flush_txfifo(s); + ospi_disable_cs(s); + + fifo8_reset(&s->rx_fifo); +} + +static void flash_cmd_ctrl_mem_reg_post_write(RegisterInfo *reg, + uint64_t val) +{ + XlnxVersalOspi *s = XILINX_VERSAL_OSPI(reg->opaque); + if (ARRAY_FIELD_EX32(s->regs, CONFIG_REG, ENB_SPI_FLD)) { + if (ARRAY_FIELD_EX32(s->regs, + FLASH_COMMAND_CTRL_MEM_REG, + TRIGGER_MEM_BANK_REQ_FLD)) { + ospi_stig_membank_req(s); + ARRAY_FIELD_DP32(s->regs, FLASH_COMMAND_CTRL_MEM_REG, + TRIGGER_MEM_BANK_REQ_FLD, 0); + } + } +} + +static void flash_cmd_ctrl_reg_post_write(RegisterInfo *reg, uint64_t val) +{ + XlnxVersalOspi *s = XILINX_VERSAL_OSPI(reg->opaque); + + if (ARRAY_FIELD_EX32(s->regs, CONFIG_REG, ENB_SPI_FLD) && + ARRAY_FIELD_EX32(s->regs, FLASH_CMD_CTRL_REG, CMD_EXEC_FLD)) { + ospi_stig_cmd_exec(s); + set_irq(s, R_IRQ_STATUS_REG_STIG_REQ_INT_FLD_MASK); + ARRAY_FIELD_DP32(s->regs, FLASH_CMD_CTRL_REG, CMD_EXEC_FLD, 0); + } +} + +static uint64_t ind_wr_dec_num_done(XlnxVersalOspi *s, uint64_t val) +{ + unsigned int done = ARRAY_FIELD_EX32(s->regs, INDIRECT_WRITE_XFER_CTRL_REG, + NUM_IND_OPS_DONE_FLD); + done--; + done &= 0x3; + val = FIELD_DP32(val, INDIRECT_WRITE_XFER_CTRL_REG, + NUM_IND_OPS_DONE_FLD, done); + return val; +} + +static bool ind_wr_clearing_op_done(XlnxVersalOspi *s, uint64_t new_val) +{ + bool set_in_reg = ARRAY_FIELD_EX32(s->regs, INDIRECT_WRITE_XFER_CTRL_REG, + IND_OPS_DONE_STATUS_FLD); + bool set_in_new_val = FIELD_EX32(new_val, INDIRECT_WRITE_XFER_CTRL_REG, + IND_OPS_DONE_STATUS_FLD); + /* return true if clearing bit */ + return set_in_reg && !set_in_new_val; +} + +static uint64_t ind_wr_xfer_ctrl_reg_pre_write(RegisterInfo *reg, + uint64_t val) +{ + XlnxVersalOspi *s = XILINX_VERSAL_OSPI(reg->opaque); + + if (ind_wr_clearing_op_done(s, val)) { + val = ind_wr_dec_num_done(s, val); + } + return val; +} + +static void ind_wr_xfer_ctrl_reg_post_write(RegisterInfo *reg, uint64_t val) +{ + XlnxVersalOspi *s = XILINX_VERSAL_OSPI(reg->opaque); + + if (s->ind_write_disabled) { + return; + } + + if (ARRAY_FIELD_EX32(s->regs, INDIRECT_WRITE_XFER_CTRL_REG, START_FLD)) { + ospi_ind_op_queue_up_wr(s); + ospi_do_indirect_write(s); + ARRAY_FIELD_DP32(s->regs, INDIRECT_WRITE_XFER_CTRL_REG, START_FLD, 0); + } + + if (ARRAY_FIELD_EX32(s->regs, INDIRECT_WRITE_XFER_CTRL_REG, CANCEL_FLD)) { + ospi_ind_op_cancel(s->wr_ind_op); + fifo8_reset(&s->tx_sram); + ARRAY_FIELD_DP32(s->regs, INDIRECT_WRITE_XFER_CTRL_REG, CANCEL_FLD, 0); + } +} + +static uint64_t ind_wr_xfer_ctrl_reg_post_read(RegisterInfo *reg, + uint64_t val) +{ + XlnxVersalOspi *s = XILINX_VERSAL_OSPI(reg->opaque); + IndOp *op = s->wr_ind_op; + + /* Check if ind ops is ongoing */ + if (!ospi_ind_op_completed(&op[0])) { + /* Check if two ind ops are queued */ + if (!ospi_ind_op_completed(&op[1])) { + val = FIELD_DP32(val, INDIRECT_WRITE_XFER_CTRL_REG, + WR_QUEUED_FLD, 1); + } + val = FIELD_DP32(val, INDIRECT_WRITE_XFER_CTRL_REG, WR_STATUS_FLD, 1); + } + return val; +} + +static uint64_t ind_rd_dec_num_done(XlnxVersalOspi *s, uint64_t val) +{ + unsigned int done = ARRAY_FIELD_EX32(s->regs, INDIRECT_READ_XFER_CTRL_REG, + NUM_IND_OPS_DONE_FLD); + done--; + done &= 0x3; + val = FIELD_DP32(val, INDIRECT_READ_XFER_CTRL_REG, + NUM_IND_OPS_DONE_FLD, done); + return val; +} + +static uint64_t ind_rd_xfer_ctrl_reg_pre_write(RegisterInfo *reg, + uint64_t val) +{ + XlnxVersalOspi *s = XILINX_VERSAL_OSPI(reg->opaque); + + if (FIELD_EX32(val, INDIRECT_READ_XFER_CTRL_REG, + IND_OPS_DONE_STATUS_FLD)) { + val = ind_rd_dec_num_done(s, val); + val &= ~R_INDIRECT_READ_XFER_CTRL_REG_IND_OPS_DONE_STATUS_FLD_MASK; + } + return val; +} + +static void ind_rd_xfer_ctrl_reg_post_write(RegisterInfo *reg, uint64_t val) +{ + XlnxVersalOspi *s = XILINX_VERSAL_OSPI(reg->opaque); + + if (ARRAY_FIELD_EX32(s->regs, INDIRECT_READ_XFER_CTRL_REG, START_FLD)) { + ospi_ind_op_queue_up_rd(s); + ospi_do_ind_read(s); + ARRAY_FIELD_DP32(s->regs, INDIRECT_READ_XFER_CTRL_REG, START_FLD, 0); + } + + if (ARRAY_FIELD_EX32(s->regs, INDIRECT_READ_XFER_CTRL_REG, CANCEL_FLD)) { + ospi_ind_op_cancel(s->rd_ind_op); + fifo8_reset(&s->rx_sram); + ARRAY_FIELD_DP32(s->regs, INDIRECT_READ_XFER_CTRL_REG, CANCEL_FLD, 0); + } +} + +static uint64_t ind_rd_xfer_ctrl_reg_post_read(RegisterInfo *reg, + uint64_t val) +{ + XlnxVersalOspi *s = XILINX_VERSAL_OSPI(reg->opaque); + IndOp *op = s->rd_ind_op; + + /* Check if ind ops is ongoing */ + if (!ospi_ind_op_completed(&op[0])) { + /* Check if two ind ops are queued */ + if (!ospi_ind_op_completed(&op[1])) { + val = FIELD_DP32(val, INDIRECT_READ_XFER_CTRL_REG, + RD_QUEUED_FLD, 1); + } + val = FIELD_DP32(val, INDIRECT_READ_XFER_CTRL_REG, RD_STATUS_FLD, 1); + } + return val; +} + +static uint64_t sram_fill_reg_post_read(RegisterInfo *reg, uint64_t val) +{ + XlnxVersalOspi *s = XILINX_VERSAL_OSPI(reg->opaque); + val = ((fifo8_num_used(&s->tx_sram) & 0xFFFF) << 16) | + (fifo8_num_used(&s->rx_sram) & 0xFFFF); + return val; +} + +static uint64_t dll_obs_upper_reg_post_read(RegisterInfo *reg, uint64_t val) +{ + XlnxVersalOspi *s = XILINX_VERSAL_OSPI(reg->opaque); + uint32_t rx_dec_out; + + rx_dec_out = FIELD_EX32(val, DLL_OBSERVABLE_UPPER_REG, + DLL_OBSERVABLE__UPPER_RX_DECODER_OUTPUT_FLD); + + if (rx_dec_out < MAX_RX_DEC_OUT) { + ARRAY_FIELD_DP32(s->regs, DLL_OBSERVABLE_UPPER_REG, + DLL_OBSERVABLE__UPPER_RX_DECODER_OUTPUT_FLD, + rx_dec_out + 1); + } + + return val; +} + + +static void xlnx_versal_ospi_reset(DeviceState *dev) +{ + XlnxVersalOspi *s = XILINX_VERSAL_OSPI(dev); + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(s->regs_info); ++i) { + register_reset(&s->regs_info[i]); + } + + fifo8_reset(&s->rx_fifo); + fifo8_reset(&s->tx_fifo); + fifo8_reset(&s->rx_sram); + fifo8_reset(&s->tx_sram); + + s->rd_ind_op[0].completed = true; + s->rd_ind_op[1].completed = true; + s->wr_ind_op[0].completed = true; + s->wr_ind_op[1].completed = true; + ARRAY_FIELD_DP32(s->regs, DLL_OBSERVABLE_LOWER_REG, + DLL_OBSERVABLE_LOWER_DLL_LOCK_FLD, 1); + ARRAY_FIELD_DP32(s->regs, DLL_OBSERVABLE_LOWER_REG, + DLL_OBSERVABLE_LOWER_LOOPBACK_LOCK_FLD, 1); +} + +static RegisterAccessInfo ospi_regs_info[] = { + { .name = "CONFIG_REG", + .addr = A_CONFIG_REG, + .reset = 0x80780081, + .ro = 0x9c000000, + },{ .name = "DEV_INSTR_RD_CONFIG_REG", + .addr = A_DEV_INSTR_RD_CONFIG_REG, + .reset = 0x3, + .ro = 0xe0ecc800, + },{ .name = "DEV_INSTR_WR_CONFIG_REG", + .addr = A_DEV_INSTR_WR_CONFIG_REG, + .reset = 0x2, + .ro = 0xe0fcce00, + },{ .name = "DEV_DELAY_REG", + .addr = A_DEV_DELAY_REG, + },{ .name = "RD_DATA_CAPTURE_REG", + .addr = A_RD_DATA_CAPTURE_REG, + .reset = 0x1, + .ro = 0xfff0fec0, + },{ .name = "DEV_SIZE_CONFIG_REG", + .addr = A_DEV_SIZE_CONFIG_REG, + .reset = 0x101002, + .ro = 0xe0000000, + },{ .name = "SRAM_PARTITION_CFG_REG", + .addr = A_SRAM_PARTITION_CFG_REG, + .reset = 0x80, + .ro = 0xffffff00, + },{ .name = "IND_AHB_ADDR_TRIGGER_REG", + .addr = A_IND_AHB_ADDR_TRIGGER_REG, + },{ .name = "DMA_PERIPH_CONFIG_REG", + .addr = A_DMA_PERIPH_CONFIG_REG, + .ro = 0xfffff0f0, + },{ .name = "REMAP_ADDR_REG", + .addr = A_REMAP_ADDR_REG, + },{ .name = "MODE_BIT_CONFIG_REG", + .addr = A_MODE_BIT_CONFIG_REG, + .reset = 0x200, + .ro = 0xffff7800, + },{ .name = "SRAM_FILL_REG", + .addr = A_SRAM_FILL_REG, + .ro = 0xffffffff, + .post_read = sram_fill_reg_post_read, + },{ .name = "TX_THRESH_REG", + .addr = A_TX_THRESH_REG, + .reset = 0x1, + .ro = 0xffffffe0, + },{ .name = "RX_THRESH_REG", + .addr = A_RX_THRESH_REG, + .reset = 0x1, + .ro = 0xffffffe0, + },{ .name = "WRITE_COMPLETION_CTRL_REG", + .addr = A_WRITE_COMPLETION_CTRL_REG, + .reset = 0x10005, + .ro = 0x1800, + },{ .name = "NO_OF_POLLS_BEF_EXP_REG", + .addr = A_NO_OF_POLLS_BEF_EXP_REG, + .reset = 0xffffffff, + },{ .name = "IRQ_STATUS_REG", + .addr = A_IRQ_STATUS_REG, + .ro = 0xfff08000, + .w1c = 0xf7fff, + },{ .name = "IRQ_MASK_REG", + .addr = A_IRQ_MASK_REG, + .ro = 0xfff08000, + },{ .name = "LOWER_WR_PROT_REG", + .addr = A_LOWER_WR_PROT_REG, + },{ .name = "UPPER_WR_PROT_REG", + .addr = A_UPPER_WR_PROT_REG, + },{ .name = "WR_PROT_CTRL_REG", + .addr = A_WR_PROT_CTRL_REG, + .ro = 0xfffffffc, + },{ .name = "INDIRECT_READ_XFER_CTRL_REG", + .addr = A_INDIRECT_READ_XFER_CTRL_REG, + .ro = 0xffffffd4, + .w1c = 0x08, + .pre_write = ind_rd_xfer_ctrl_reg_pre_write, + .post_write = ind_rd_xfer_ctrl_reg_post_write, + .post_read = ind_rd_xfer_ctrl_reg_post_read, + },{ .name = "INDIRECT_READ_XFER_WATERMARK_REG", + .addr = A_INDIRECT_READ_XFER_WATERMARK_REG, + },{ .name = "INDIRECT_READ_XFER_START_REG", + .addr = A_INDIRECT_READ_XFER_START_REG, + },{ .name = "INDIRECT_READ_XFER_NUM_BYTES_REG", + .addr = A_INDIRECT_READ_XFER_NUM_BYTES_REG, + },{ .name = "INDIRECT_WRITE_XFER_CTRL_REG", + .addr = A_INDIRECT_WRITE_XFER_CTRL_REG, + .ro = 0xffffffdc, + .w1c = 0x20, + .pre_write = ind_wr_xfer_ctrl_reg_pre_write, + .post_write = ind_wr_xfer_ctrl_reg_post_write, + .post_read = ind_wr_xfer_ctrl_reg_post_read, + },{ .name = "INDIRECT_WRITE_XFER_WATERMARK_REG", + .addr = A_INDIRECT_WRITE_XFER_WATERMARK_REG, + .reset = 0xffffffff, + },{ .name = "INDIRECT_WRITE_XFER_START_REG", + .addr = A_INDIRECT_WRITE_XFER_START_REG, + },{ .name = "INDIRECT_WRITE_XFER_NUM_BYTES_REG", + .addr = A_INDIRECT_WRITE_XFER_NUM_BYTES_REG, + },{ .name = "INDIRECT_TRIGGER_ADDR_RANGE_REG", + .addr = A_INDIRECT_TRIGGER_ADDR_RANGE_REG, + .reset = 0x4, + .ro = 0xfffffff0, + },{ .name = "FLASH_COMMAND_CTRL_MEM_REG", + .addr = A_FLASH_COMMAND_CTRL_MEM_REG, + .ro = 0xe008fffe, + .post_write = flash_cmd_ctrl_mem_reg_post_write, + },{ .name = "FLASH_CMD_CTRL_REG", + .addr = A_FLASH_CMD_CTRL_REG, + .ro = 0x7a, + .post_write = flash_cmd_ctrl_reg_post_write, + },{ .name = "FLASH_CMD_ADDR_REG", + .addr = A_FLASH_CMD_ADDR_REG, + },{ .name = "FLASH_RD_DATA_LOWER_REG", + .addr = A_FLASH_RD_DATA_LOWER_REG, + .ro = 0xffffffff, + },{ .name = "FLASH_RD_DATA_UPPER_REG", + .addr = A_FLASH_RD_DATA_UPPER_REG, + .ro = 0xffffffff, + },{ .name = "FLASH_WR_DATA_LOWER_REG", + .addr = A_FLASH_WR_DATA_LOWER_REG, + },{ .name = "FLASH_WR_DATA_UPPER_REG", + .addr = A_FLASH_WR_DATA_UPPER_REG, + },{ .name = "POLLING_FLASH_STATUS_REG", + .addr = A_POLLING_FLASH_STATUS_REG, + .ro = 0xfff0ffff, + },{ .name = "PHY_CONFIGURATION_REG", + .addr = A_PHY_CONFIGURATION_REG, + .reset = 0x40000000, + .ro = 0x1f80ff80, + },{ .name = "PHY_MASTER_CONTROL_REG", + .addr = A_PHY_MASTER_CONTROL_REG, + .reset = 0x800000, + .ro = 0xfe08ff80, + },{ .name = "DLL_OBSERVABLE_LOWER_REG", + .addr = A_DLL_OBSERVABLE_LOWER_REG, + .ro = 0xffffffff, + },{ .name = "DLL_OBSERVABLE_UPPER_REG", + .addr = A_DLL_OBSERVABLE_UPPER_REG, + .ro = 0xffffffff, + .post_read = dll_obs_upper_reg_post_read, + },{ .name = "OPCODE_EXT_LOWER_REG", + .addr = A_OPCODE_EXT_LOWER_REG, + .reset = 0x13edfa00, + },{ .name = "OPCODE_EXT_UPPER_REG", + .addr = A_OPCODE_EXT_UPPER_REG, + .reset = 0x6f90000, + .ro = 0xffff, + },{ .name = "MODULE_ID_REG", + .addr = A_MODULE_ID_REG, + .reset = 0x300, + .ro = 0xffffffff, + } +}; + +/* Return dev-obj from reg-region created by register_init_block32 */ +static XlnxVersalOspi *xilinx_ospi_of_mr(void *mr_accessor) +{ + RegisterInfoArray *reg_array = mr_accessor; + Object *dev; + + dev = reg_array->mem.owner; + assert(dev); + + return XILINX_VERSAL_OSPI(dev); +} + +static void ospi_write(void *opaque, hwaddr addr, uint64_t value, + unsigned int size) +{ + XlnxVersalOspi *s = xilinx_ospi_of_mr(opaque); + + register_write_memory(opaque, addr, value, size); + ospi_update_irq_line(s); +} + +static const MemoryRegionOps ospi_ops = { + .read = register_read_memory, + .write = ospi_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static uint64_t ospi_indac_read(void *opaque, unsigned int size) +{ + XlnxVersalOspi *s = XILINX_VERSAL_OSPI(opaque); + uint64_t ret = ospi_rx_sram_read(s, size); + + if (!ospi_ind_op_completed(s->rd_ind_op)) { + ospi_do_ind_read(s); + } + return ret; +} + +static void ospi_indac_write(void *opaque, uint64_t value, unsigned int size) +{ + XlnxVersalOspi *s = XILINX_VERSAL_OSPI(opaque); + + g_assert(!s->ind_write_disabled); + + if (!ospi_ind_op_completed(s->wr_ind_op)) { + ospi_tx_sram_write(s, value, size); + ospi_do_indirect_write(s); + } else { + qemu_log_mask(LOG_GUEST_ERROR, + "OSPI wr into indac area while no ongoing indac wr\n"); + } +} + +static bool is_inside_indac_range(XlnxVersalOspi *s, hwaddr addr) +{ + uint32_t range_start; + uint32_t range_end; + + if (ARRAY_FIELD_EX32(s->regs, CONFIG_REG, ENB_DMA_IF_FLD)) { + return true; + } + + range_start = s->regs[R_IND_AHB_ADDR_TRIGGER_REG]; + range_end = range_start + + (1 << ARRAY_FIELD_EX32(s->regs, + INDIRECT_TRIGGER_ADDR_RANGE_REG, + IND_RANGE_WIDTH_FLD)); + + addr += s->regs[R_IND_AHB_ADDR_TRIGGER_REG] & 0xF0000000; + + return addr >= range_start && addr < range_end; +} + +static bool ospi_is_indac_active(XlnxVersalOspi *s) +{ + /* + * When dac and indac cannot be active at the same time, + * return true when dac is disabled. + */ + return s->dac_with_indac || !s->dac_enable; +} + +static uint64_t ospi_dac_read(void *opaque, hwaddr addr, unsigned int size) +{ + XlnxVersalOspi *s = XILINX_VERSAL_OSPI(opaque); + + if (ARRAY_FIELD_EX32(s->regs, CONFIG_REG, ENB_SPI_FLD)) { + if (ospi_is_indac_active(s) && + is_inside_indac_range(s, addr)) { + return ospi_indac_read(s, size); + } + if (ARRAY_FIELD_EX32(s->regs, CONFIG_REG, ENB_DIR_ACC_CTLR_FLD) + && s->dac_enable) { + if (ARRAY_FIELD_EX32(s->regs, + CONFIG_REG, ENB_AHB_ADDR_REMAP_FLD)) { + addr += s->regs[R_REMAP_ADDR_REG]; + } + return ospi_do_dac_read(opaque, addr, size); + } else { + qemu_log_mask(LOG_GUEST_ERROR, "OSPI AHB rd while DAC disabled\n"); + } + } else { + qemu_log_mask(LOG_GUEST_ERROR, "OSPI AHB rd while OSPI disabled\n"); + } + + return 0; +} + +static void ospi_dac_write(void *opaque, hwaddr addr, uint64_t value, + unsigned int size) +{ + XlnxVersalOspi *s = XILINX_VERSAL_OSPI(opaque); + + if (ARRAY_FIELD_EX32(s->regs, CONFIG_REG, ENB_SPI_FLD)) { + if (ospi_is_indac_active(s) && + !s->ind_write_disabled && + is_inside_indac_range(s, addr)) { + return ospi_indac_write(s, value, size); + } + if (ARRAY_FIELD_EX32(s->regs, CONFIG_REG, ENB_DIR_ACC_CTLR_FLD) && + s->dac_enable) { + if (ARRAY_FIELD_EX32(s->regs, + CONFIG_REG, ENB_AHB_ADDR_REMAP_FLD)) { + addr += s->regs[R_REMAP_ADDR_REG]; + } + /* Check if addr is write protected */ + if (ARRAY_FIELD_EX32(s->regs, WR_PROT_CTRL_REG, ENB_FLD) && + ospi_is_write_protected(s, addr)) { + set_irq(s, R_IRQ_STATUS_REG_PROT_WR_ATTEMPT_FLD_MASK); + ospi_update_irq_line(s); + qemu_log_mask(LOG_GUEST_ERROR, + "OSPI writing into write protected area\n"); + return; + } + ospi_do_dac_write(opaque, addr, value, size); + } else { + qemu_log_mask(LOG_GUEST_ERROR, "OSPI AHB wr while DAC disabled\n"); + } + } else { + qemu_log_mask(LOG_GUEST_ERROR, "OSPI AHB wr while OSPI disabled\n"); + } +} + +static const MemoryRegionOps ospi_dac_ops = { + .read = ospi_dac_read, + .write = ospi_dac_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static void ospi_update_dac_status(void *opaque, int n, int level) +{ + XlnxVersalOspi *s = XILINX_VERSAL_OSPI(opaque); + + s->dac_enable = level; +} + +static void xlnx_versal_ospi_realize(DeviceState *dev, Error **errp) +{ + XlnxVersalOspi *s = XILINX_VERSAL_OSPI(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + + s->num_cs = 4; + s->spi = ssi_create_bus(dev, "spi0"); + s->cs_lines = g_new0(qemu_irq, s->num_cs); + for (int i = 0; i < s->num_cs; ++i) { + sysbus_init_irq(sbd, &s->cs_lines[i]); + } + + fifo8_create(&s->rx_fifo, RXFF_SZ); + fifo8_create(&s->tx_fifo, TXFF_SZ); + fifo8_create(&s->rx_sram, RXFF_SZ); + fifo8_create(&s->tx_sram, TXFF_SZ); +} + +static void xlnx_versal_ospi_init(Object *obj) +{ + XlnxVersalOspi *s = XILINX_VERSAL_OSPI(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + DeviceState *dev = DEVICE(obj); + RegisterInfoArray *reg_array; + + memory_region_init(&s->iomem, obj, TYPE_XILINX_VERSAL_OSPI, + XILINX_VERSAL_OSPI_R_MAX * 4); + reg_array = + register_init_block32(DEVICE(obj), ospi_regs_info, + ARRAY_SIZE(ospi_regs_info), + s->regs_info, s->regs, + &ospi_ops, + XILINX_VERSAL_OSPI_ERR_DEBUG, + XILINX_VERSAL_OSPI_R_MAX * 4); + memory_region_add_subregion(&s->iomem, 0x0, ®_array->mem); + sysbus_init_mmio(sbd, &s->iomem); + + memory_region_init_io(&s->iomem_dac, obj, &ospi_dac_ops, s, + TYPE_XILINX_VERSAL_OSPI "-dac", 0x20000000); + sysbus_init_mmio(sbd, &s->iomem_dac); + + sysbus_init_irq(sbd, &s->irq); + + object_property_add_link(obj, "dma-src", TYPE_XLNX_CSU_DMA, + (Object **)&s->dma_src, + object_property_allow_set_link, + OBJ_PROP_LINK_STRONG); + + qdev_init_gpio_in_named(dev, ospi_update_dac_status, "ospi-mux-sel", 1); +} + +static const VMStateDescription vmstate_ind_op = { + .name = "OSPIIndOp", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(flash_addr, IndOp), + VMSTATE_UINT32(num_bytes, IndOp), + VMSTATE_UINT32(done_bytes, IndOp), + VMSTATE_BOOL(completed, IndOp), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_xlnx_versal_ospi = { + .name = TYPE_XILINX_VERSAL_OSPI, + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_FIFO8(rx_fifo, XlnxVersalOspi), + VMSTATE_FIFO8(tx_fifo, XlnxVersalOspi), + VMSTATE_FIFO8(rx_sram, XlnxVersalOspi), + VMSTATE_FIFO8(tx_sram, XlnxVersalOspi), + VMSTATE_BOOL(ind_write_disabled, XlnxVersalOspi), + VMSTATE_BOOL(dac_with_indac, XlnxVersalOspi), + VMSTATE_BOOL(dac_enable, XlnxVersalOspi), + VMSTATE_BOOL(src_dma_inprog, XlnxVersalOspi), + VMSTATE_STRUCT_ARRAY(rd_ind_op, XlnxVersalOspi, 2, 1, + vmstate_ind_op, IndOp), + VMSTATE_STRUCT_ARRAY(wr_ind_op, XlnxVersalOspi, 2, 1, + vmstate_ind_op, IndOp), + VMSTATE_UINT32_ARRAY(regs, XlnxVersalOspi, XILINX_VERSAL_OSPI_R_MAX), + VMSTATE_UINT8_ARRAY(stig_membank, XlnxVersalOspi, 512), + VMSTATE_END_OF_LIST(), + } +}; + +static Property xlnx_versal_ospi_properties[] = { + DEFINE_PROP_BOOL("dac-with-indac", XlnxVersalOspi, dac_with_indac, false), + DEFINE_PROP_BOOL("indac-write-disabled", XlnxVersalOspi, + ind_write_disabled, false), + DEFINE_PROP_END_OF_LIST(), +}; + +static void xlnx_versal_ospi_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = xlnx_versal_ospi_reset; + dc->realize = xlnx_versal_ospi_realize; + dc->vmsd = &vmstate_xlnx_versal_ospi; + device_class_set_props(dc, xlnx_versal_ospi_properties); +} + +static const TypeInfo xlnx_versal_ospi_info = { + .name = TYPE_XILINX_VERSAL_OSPI, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(XlnxVersalOspi), + .class_init = xlnx_versal_ospi_class_init, + .instance_init = xlnx_versal_ospi_init, +}; + +static void xlnx_versal_ospi_register_types(void) +{ + type_register_static(&xlnx_versal_ospi_info); +} + +type_init(xlnx_versal_ospi_register_types) diff --git a/include/hw/ssi/xlnx-versal-ospi.h b/include/hw/ssi/xlnx-versal-ospi.h new file mode 100644 index 0000000000..14d1263497 --- /dev/null +++ b/include/hw/ssi/xlnx-versal-ospi.h @@ -0,0 +1,111 @@ +/* + * Header file for the Xilinx Versal's OSPI controller + * + * Copyright (C) 2021 Xilinx Inc + * Written by Francisco 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. + */ + +/* + * This is a model of Xilinx Versal's Octal SPI flash memory controller + * documented in Versal's Technical Reference manual [1] and the Versal ACAP + * Register reference [2]. + * + * References: + * + * [1] Versal ACAP Technical Reference Manual, + * https://www.xilinx.com/support/documentation/architecture-manuals/am011-versal-acap-trm.pdf + * + * [2] Versal ACAP Register Reference, + * https://www.xilinx.com/html_docs/registers/am012/am012-versal-register-reference.html#mod___ospi.html + * + * + * QEMU interface: + * + sysbus MMIO region 0: MemoryRegion for the device's registers + * + sysbus MMIO region 1: MemoryRegion for flash memory linear address space + * (data transfer). + * + sysbus IRQ 0: Device interrupt. + * + Named GPIO input "ospi-mux-sel": 0: enables indirect access mode + * and 1: enables direct access mode. + * + Property "dac-with-indac": Allow both direct accesses and indirect + * accesses simultaneously. + * + Property "indac-write-disabled": Disable indirect access writes. + */ + +#ifndef XILINX_VERSAL_OSPI_H +#define XILINX_VERSAL_OSPI_H + +#include "hw/register.h" +#include "hw/ssi/ssi.h" +#include "qemu/fifo8.h" +#include "hw/dma/xlnx_csu_dma.h" + +#define TYPE_XILINX_VERSAL_OSPI "xlnx.versal-ospi" + +OBJECT_DECLARE_SIMPLE_TYPE(XlnxVersalOspi, XILINX_VERSAL_OSPI) + +#define XILINX_VERSAL_OSPI_R_MAX (0xfc / 4 + 1) + +/* + * Indirect operations + */ +typedef struct IndOp { + uint32_t flash_addr; + uint32_t num_bytes; + uint32_t done_bytes; + bool completed; +} IndOp; + +struct XlnxVersalOspi { + SysBusDevice parent_obj; + + MemoryRegion iomem; + MemoryRegion iomem_dac; + + uint8_t num_cs; + qemu_irq *cs_lines; + + SSIBus *spi; + + Fifo8 rx_fifo; + Fifo8 tx_fifo; + + Fifo8 rx_sram; + Fifo8 tx_sram; + + qemu_irq irq; + + XlnxCSUDMA *dma_src; + bool ind_write_disabled; + bool dac_with_indac; + bool dac_enable; + bool src_dma_inprog; + + IndOp rd_ind_op[2]; + IndOp wr_ind_op[2]; + + uint32_t regs[XILINX_VERSAL_OSPI_R_MAX]; + RegisterInfo regs_info[XILINX_VERSAL_OSPI_R_MAX]; + + /* Maximum inferred membank size is 512 bytes */ + uint8_t stig_membank[512]; +}; + +#endif /* XILINX_VERSAL_OSPI_H */ From 868d96800477a3dfa5d0e0f6cb3a88c3b1fab6a3 Mon Sep 17 00:00:00 2001 From: Francisco Iglesias Date: Fri, 21 Jan 2022 16:11:38 +0000 Subject: [PATCH 082/460] hw/arm/xlnx-versal: Connect the OSPI flash memory controller model Connect the OSPI flash memory controller model (including the source and destination DMA). Signed-off-by: Francisco Iglesias Reviewed-by: Peter Maydell Message-id: 20220121161141.14389-8-francisco.iglesias@xilinx.com Signed-off-by: Peter Maydell --- hw/arm/xlnx-versal.c | 93 ++++++++++++++++++++++++++++++++++++ include/hw/arm/xlnx-versal.h | 20 ++++++++ 2 files changed, 113 insertions(+) diff --git a/hw/arm/xlnx-versal.c b/hw/arm/xlnx-versal.c index c8c0c102c7..ab58bebfd2 100644 --- a/hw/arm/xlnx-versal.c +++ b/hw/arm/xlnx-versal.c @@ -28,6 +28,7 @@ #define GEM_REVISION 0x40070106 #define VERSAL_NUM_PMC_APB_IRQS 3 +#define NUM_OSPI_IRQ_LINES 3 static void versal_create_apu_cpus(Versal *s) { @@ -412,6 +413,97 @@ static void versal_create_pmc_iou_slcr(Versal *s, qemu_irq *pic) qdev_get_gpio_in(DEVICE(&s->pmc.apb_irq_orgate), 2)); } +static void versal_create_ospi(Versal *s, qemu_irq *pic) +{ + SysBusDevice *sbd; + MemoryRegion *mr_dac; + qemu_irq ospi_mux_sel; + DeviceState *orgate; + + memory_region_init(&s->pmc.iou.ospi.linear_mr, OBJECT(s), + "versal-ospi-linear-mr" , MM_PMC_OSPI_DAC_SIZE); + + object_initialize_child(OBJECT(s), "versal-ospi", &s->pmc.iou.ospi.ospi, + TYPE_XILINX_VERSAL_OSPI); + + mr_dac = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->pmc.iou.ospi.ospi), 1); + memory_region_add_subregion(&s->pmc.iou.ospi.linear_mr, 0x0, mr_dac); + + /* Create the OSPI destination DMA */ + object_initialize_child(OBJECT(s), "versal-ospi-dma-dst", + &s->pmc.iou.ospi.dma_dst, + TYPE_XLNX_CSU_DMA); + + object_property_set_link(OBJECT(&s->pmc.iou.ospi.dma_dst), + "dma", OBJECT(get_system_memory()), + &error_abort); + + sbd = SYS_BUS_DEVICE(&s->pmc.iou.ospi.dma_dst); + sysbus_realize(sbd, &error_fatal); + + memory_region_add_subregion(&s->mr_ps, MM_PMC_OSPI_DMA_DST, + sysbus_mmio_get_region(sbd, 0)); + + /* Create the OSPI source DMA */ + object_initialize_child(OBJECT(s), "versal-ospi-dma-src", + &s->pmc.iou.ospi.dma_src, + TYPE_XLNX_CSU_DMA); + + object_property_set_bool(OBJECT(&s->pmc.iou.ospi.dma_src), "is-dst", + false, &error_abort); + + object_property_set_link(OBJECT(&s->pmc.iou.ospi.dma_src), + "dma", OBJECT(mr_dac), &error_abort); + + object_property_set_link(OBJECT(&s->pmc.iou.ospi.dma_src), + "stream-connected-dma", + OBJECT(&s->pmc.iou.ospi.dma_dst), + &error_abort); + + sbd = SYS_BUS_DEVICE(&s->pmc.iou.ospi.dma_src); + sysbus_realize(sbd, &error_fatal); + + memory_region_add_subregion(&s->mr_ps, MM_PMC_OSPI_DMA_SRC, + sysbus_mmio_get_region(sbd, 0)); + + /* Realize the OSPI */ + object_property_set_link(OBJECT(&s->pmc.iou.ospi.ospi), "dma-src", + OBJECT(&s->pmc.iou.ospi.dma_src), &error_abort); + + sbd = SYS_BUS_DEVICE(&s->pmc.iou.ospi.ospi); + sysbus_realize(sbd, &error_fatal); + + memory_region_add_subregion(&s->mr_ps, MM_PMC_OSPI, + sysbus_mmio_get_region(sbd, 0)); + + memory_region_add_subregion(&s->mr_ps, MM_PMC_OSPI_DAC, + &s->pmc.iou.ospi.linear_mr); + + /* ospi_mux_sel */ + ospi_mux_sel = qdev_get_gpio_in_named(DEVICE(&s->pmc.iou.ospi.ospi), + "ospi-mux-sel", 0); + qdev_connect_gpio_out_named(DEVICE(&s->pmc.iou.slcr), "ospi-mux-sel", 0, + ospi_mux_sel); + + /* OSPI irq */ + object_initialize_child(OBJECT(s), "ospi-irq-orgate", + &s->pmc.iou.ospi.irq_orgate, TYPE_OR_IRQ); + object_property_set_int(OBJECT(&s->pmc.iou.ospi.irq_orgate), + "num-lines", NUM_OSPI_IRQ_LINES, &error_fatal); + + orgate = DEVICE(&s->pmc.iou.ospi.irq_orgate); + qdev_realize(orgate, NULL, &error_fatal); + + sysbus_connect_irq(SYS_BUS_DEVICE(&s->pmc.iou.ospi.ospi), 0, + qdev_get_gpio_in(orgate, 0)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->pmc.iou.ospi.dma_src), 0, + qdev_get_gpio_in(orgate, 1)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->pmc.iou.ospi.dma_dst), 0, + qdev_get_gpio_in(orgate, 2)); + + qdev_connect_gpio_out(orgate, 0, pic[VERSAL_OSPI_IRQ]); +} + /* This takes the board allocated linear DDR memory and creates aliases * for each split DDR range/aperture on the Versal address map. */ @@ -552,6 +644,7 @@ static void versal_realize(DeviceState *dev, Error **errp) versal_create_bbram(s, pic); versal_create_efuse(s, pic); versal_create_pmc_iou_slcr(s, pic); + versal_create_ospi(s, pic); versal_map_ddr(s); versal_unimp(s); diff --git a/include/hw/arm/xlnx-versal.h b/include/hw/arm/xlnx-versal.h index 811df73350..1b5ad4de80 100644 --- a/include/hw/arm/xlnx-versal.h +++ b/include/hw/arm/xlnx-versal.h @@ -26,6 +26,8 @@ #include "hw/misc/xlnx-versal-xramc.h" #include "hw/nvram/xlnx-bbram.h" #include "hw/nvram/xlnx-versal-efuse.h" +#include "hw/ssi/xlnx-versal-ospi.h" +#include "hw/dma/xlnx_csu_dma.h" #include "hw/misc/xlnx-versal-pmc-iou-slcr.h" #define TYPE_XLNX_VERSAL "xlnx-versal" @@ -80,6 +82,14 @@ struct Versal { struct { SDHCIState sd[XLNX_VERSAL_NR_SDS]; XlnxVersalPmcIouSlcr slcr; + + struct { + XlnxVersalOspi ospi; + XlnxCSUDMA dma_src; + XlnxCSUDMA dma_dst; + MemoryRegion linear_mr; + qemu_or_irq irq_orgate; + } ospi; } iou; XlnxZynqMPRTC rtc; @@ -116,6 +126,7 @@ struct Versal { #define VERSAL_ADMA_IRQ_0 60 #define VERSAL_XRAM_IRQ_0 79 #define VERSAL_PMC_APB_IRQ 121 +#define VERSAL_OSPI_IRQ 124 #define VERSAL_SD0_IRQ_0 126 #define VERSAL_EFUSE_IRQ 139 #define VERSAL_RTC_ALARM_IRQ 142 @@ -184,6 +195,15 @@ struct Versal { #define MM_PMC_PMC_IOU_SLCR 0xf1060000 #define MM_PMC_PMC_IOU_SLCR_SIZE 0x10000 +#define MM_PMC_OSPI 0xf1010000 +#define MM_PMC_OSPI_SIZE 0x10000 + +#define MM_PMC_OSPI_DAC 0xc0000000 +#define MM_PMC_OSPI_DAC_SIZE 0x20000000 + +#define MM_PMC_OSPI_DMA_DST 0xf1011800 +#define MM_PMC_OSPI_DMA_SRC 0xf1011000 + #define MM_PMC_SD0 0xf1040000U #define MM_PMC_SD0_SIZE 0x10000 #define MM_PMC_BBRAM_CTRL 0xf11f0000 From 6b3fac72d91fc1363bd7d79cf14050bd12a56918 Mon Sep 17 00:00:00 2001 From: Francisco Iglesias Date: Fri, 21 Jan 2022 16:11:39 +0000 Subject: [PATCH 083/460] hw/block/m25p80: Add support for Micron Xccela flash mt35xu01g Add support for Micron Xccela flash mt35xu01g. Signed-off-by: Francisco Iglesias Reviewed-by: Edgar E. Iglesias Message-id: 20220121161141.14389-9-francisco.iglesias@xilinx.com Signed-off-by: Peter Maydell --- hw/block/m25p80.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c index b77503dc84..c6bf3c6bfa 100644 --- a/hw/block/m25p80.c +++ b/hw/block/m25p80.c @@ -255,6 +255,8 @@ static const FlashPartInfo known_devices[] = { { INFO("n25q512a", 0x20ba20, 0, 64 << 10, 1024, ER_4K) }, { INFO("n25q512ax3", 0x20ba20, 0x1000, 64 << 10, 1024, ER_4K) }, { INFO("mt25ql512ab", 0x20ba20, 0x1044, 64 << 10, 1024, ER_4K | ER_32K) }, + { INFO_STACKED("mt35xu01g", 0x2c5b1b, 0x104100, 128 << 10, 1024, + ER_4K | ER_32K, 2) }, { INFO_STACKED("n25q00", 0x20ba21, 0x1000, 64 << 10, 2048, ER_4K, 4) }, { INFO_STACKED("n25q00a", 0x20bb21, 0x1000, 64 << 10, 2048, ER_4K, 4) }, { INFO_STACKED("mt25ql01g", 0x20ba21, 0x1040, 64 << 10, 2048, ER_4K, 2) }, From 4461f0fb23bf8867c0d5a378d6b08c651c1fc7ab Mon Sep 17 00:00:00 2001 From: Francisco Iglesias Date: Fri, 21 Jan 2022 16:11:40 +0000 Subject: [PATCH 084/460] hw/arm/xlnx-versal-virt: Connect mt35xu01g flashes to the OSPI Connect Micron Xccela mt35xu01g flashes to the OSPI flash memory controller. Signed-off-by: Francisco Iglesias Reviewed-by: Edgar E. Iglesias Reviewed-by: Peter Maydell Message-id: 20220121161141.14389-10-francisco.iglesias@xilinx.com Signed-off-by: Peter Maydell --- hw/arm/xlnx-versal-virt.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/hw/arm/xlnx-versal-virt.c b/hw/arm/xlnx-versal-virt.c index 8ea9979710..3f56ae28ee 100644 --- a/hw/arm/xlnx-versal-virt.c +++ b/hw/arm/xlnx-versal-virt.c @@ -25,6 +25,8 @@ #define TYPE_XLNX_VERSAL_VIRT_MACHINE MACHINE_TYPE_NAME("xlnx-versal-virt") OBJECT_DECLARE_SIMPLE_TYPE(VersalVirt, XLNX_VERSAL_VIRT_MACHINE) +#define XLNX_VERSAL_NUM_OSPI_FLASH 4 + struct VersalVirt { MachineState parent_obj; @@ -691,6 +693,27 @@ static void versal_virt_init(MachineState *machine) exit(EXIT_FAILURE); } } + + for (i = 0; i < XLNX_VERSAL_NUM_OSPI_FLASH; i++) { + BusState *spi_bus; + DeviceState *flash_dev; + qemu_irq cs_line; + DriveInfo *dinfo = drive_get(IF_MTD, 0, i); + + spi_bus = qdev_get_child_bus(DEVICE(&s->soc.pmc.iou.ospi), "spi0"); + + flash_dev = qdev_new("mt35xu01g"); + if (dinfo) { + qdev_prop_set_drive_err(flash_dev, "drive", + blk_by_legacy_dinfo(dinfo), &error_fatal); + } + qdev_realize_and_unref(flash_dev, spi_bus, &error_fatal); + + cs_line = qdev_get_gpio_in_named(flash_dev, SSI_GPIO_CS, 0); + + sysbus_connect_irq(SYS_BUS_DEVICE(&s->soc.pmc.iou.ospi), + i + 1, cs_line); + } } static void versal_virt_machine_instance_init(Object *obj) From 72e58848b2393271f1c9866905d9c61ba21d952e Mon Sep 17 00:00:00 2001 From: Francisco Iglesias Date: Fri, 21 Jan 2022 16:11:41 +0000 Subject: [PATCH 085/460] MAINTAINERS: Add an entry for Xilinx Versal OSPI List myself as maintainer for the Xilinx Versal OSPI controller. Signed-off-by: Francisco Iglesias Reviewed-by: Edgar E. Iglesias Reviewed-by: Peter Maydell Message-id: 20220121161141.14389-11-francisco.iglesias@xilinx.com Signed-off-by: Peter Maydell --- MAINTAINERS | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index e4b3a4bcdf..6797a270e4 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -958,6 +958,12 @@ F: hw/display/dpcd.c F: include/hw/display/dpcd.h F: docs/system/arm/xlnx-versal-virt.rst +Xilinx Versal OSPI +M: Francisco Iglesias +S: Maintained +F: hw/ssi/xlnx-versal-ospi.c +F: include/hw/ssi/xlnx-versal-ospi.h + ARM ACPI Subsystem M: Shannon Zhao L: qemu-arm@nongnu.org From 5212297c474c816f65b1ad6d0d0efe88a08ba6b9 Mon Sep 17 00:00:00 2001 From: Andrew Baumann Date: Thu, 27 Jan 2022 17:50:55 +0000 Subject: [PATCH 086/460] MAINTAINERS: Remove myself (for raspi). Signed-off-by: Andrew Baumann Message-id: MW4PR21MB1940E8BB52F4053C943B1FCD9E219@MW4PR21MB1940.namprd21.prod.outlook.com Signed-off-by: Peter Maydell --- MAINTAINERS | 1 - 1 file changed, 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 6797a270e4..b43344fa98 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -818,7 +818,6 @@ F: docs/system/arm/palm.rst Raspberry Pi M: Peter Maydell -R: Andrew Baumann R: Philippe Mathieu-Daudé L: qemu-arm@nongnu.org S: Odd Fixes From 0166f5c46627b872a9d26eae8ded4be45263a321 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 9 Dec 2021 19:45:32 +0000 Subject: [PATCH 087/460] scripts: Explain the difference between linux-headers and standard-headers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If you don't know it, it's hard to figure out the difference between the linux-headers folder and the include/standard-headers folder. So let's add a short explanation to clarify the difference. Suggested-by: Thomas Huth Signed-off-by: Peter Maydell Reviewed-by: Alex Bennée Reviewed-by: Cornelia Huck Reviewed-by: Thomas Huth --- scripts/update-linux-headers.sh | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/scripts/update-linux-headers.sh b/scripts/update-linux-headers.sh index fea4d6eb65..fe850763c5 100755 --- a/scripts/update-linux-headers.sh +++ b/scripts/update-linux-headers.sh @@ -9,6 +9,22 @@ # # This work is licensed under the terms of the GNU GPL version 2. # See the COPYING file in the top-level directory. +# +# The script will copy the headers into two target folders: +# +# - linux-headers/ for files that are required for compiling for a +# Linux host. Generally we have these so we can use kernel structs +# and defines that are more recent than the headers that might be +# installed on the host system. Usually this script can do simple +# file copies for these headers. +# +# - include/standard-headers/ for files that are used for guest +# device emulation and are required on all hosts. For instance, we +# get our definitions of the virtio structures from the Linux +# kernel headers, but we need those definitions regardless of which +# host OS we are building for. This script has to be careful to +# sanitize the headers to remove any use of Linux-specifics such as +# types like "__u64". This work is done in the cp_portable function. tmpdir=$(mktemp -d) linux="$1" From fc6177af1137789dccb9e257bfae778d18381f90 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Sat, 22 Jan 2022 18:24:31 +0000 Subject: [PATCH 088/460] target/arm: Log CPU index in 'Taking exception' log MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In an SMP system it can be unclear which CPU is taking an exception; add the CPU index (which is the same value used in the TCG 'Trace %d:' logging) to the "Taking exception" log line to clarify it. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-id: 20220122182444.724087-2-peter.maydell@linaro.org --- target/arm/helper.c | 9 ++++++--- target/arm/internals.h | 2 +- target/arm/m_helper.c | 2 +- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index cfca0f5ba6..4df1239402 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -9317,8 +9317,10 @@ uint32_t arm_phys_excp_target_el(CPUState *cs, uint32_t excp_idx, return target_el; } -void arm_log_exception(int idx) +void arm_log_exception(CPUState *cs) { + int idx = cs->exception_index; + if (qemu_loglevel_mask(CPU_LOG_INT)) { const char *exc = NULL; static const char * const excnames[] = { @@ -9352,7 +9354,8 @@ void arm_log_exception(int idx) if (!exc) { exc = "unknown"; } - qemu_log_mask(CPU_LOG_INT, "Taking exception %d [%s]\n", idx, exc); + qemu_log_mask(CPU_LOG_INT, "Taking exception %d [%s] on CPU %d\n", + idx, exc, cs->cpu_index); } } @@ -10185,7 +10188,7 @@ void arm_cpu_do_interrupt(CPUState *cs) assert(!arm_feature(env, ARM_FEATURE_M)); - arm_log_exception(cs->exception_index); + arm_log_exception(cs); qemu_log_mask(CPU_LOG_INT, "...from EL%d to EL%d\n", arm_current_el(env), new_el); if (qemu_loglevel_mask(CPU_LOG_INT) diff --git a/target/arm/internals.h b/target/arm/internals.h index 89f7610ebc..3f05748ea4 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -1130,7 +1130,7 @@ bool get_phys_addr(CPUARMState *env, target_ulong address, ARMMMUFaultInfo *fi, ARMCacheAttrs *cacheattrs) __attribute__((nonnull)); -void arm_log_exception(int idx); +void arm_log_exception(CPUState *cs); #endif /* !CONFIG_USER_ONLY */ diff --git a/target/arm/m_helper.c b/target/arm/m_helper.c index 2c9922dc29..b11e927df1 100644 --- a/target/arm/m_helper.c +++ b/target/arm/m_helper.c @@ -2206,7 +2206,7 @@ void arm_v7m_cpu_do_interrupt(CPUState *cs) uint32_t lr; bool ignore_stackfaults; - arm_log_exception(cs->exception_index); + arm_log_exception(cs); /* * For exceptions we just mark as pending on the NVIC, and let that From 195209d3682847b253175af7a1cffd2c007273a2 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Sat, 22 Jan 2022 18:24:32 +0000 Subject: [PATCH 089/460] hw/intc/arm_gicv3_its: Add tracepoints MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ITS currently has no tracepoints; add a minimal set that allows basic monitoring of guest register accesses and reading of commands from the command queue. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-id: 20220122182444.724087-3-peter.maydell@linaro.org --- hw/intc/arm_gicv3_its.c | 11 +++++++++++ hw/intc/trace-events | 8 ++++++++ 2 files changed, 19 insertions(+) diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c index b2f6a8c7f0..6d2549e64b 100644 --- a/hw/intc/arm_gicv3_its.c +++ b/hw/intc/arm_gicv3_its.c @@ -13,6 +13,7 @@ #include "qemu/osdep.h" #include "qemu/log.h" +#include "trace.h" #include "hw/qdev-properties.h" #include "hw/intc/arm_gicv3_its_common.h" #include "gicv3_internal.h" @@ -634,6 +635,8 @@ static void process_cmdq(GICv3ITSState *s) cmd = (data & CMD_MASK); + trace_gicv3_its_process_command(rd_offset, cmd); + switch (cmd) { case GITS_CMD_INT: result = process_its_cmd(s, data, cq_offset, INTERRUPT); @@ -818,6 +821,8 @@ static MemTxResult gicv3_its_translation_write(void *opaque, hwaddr offset, bool result = true; uint32_t devid = 0; + trace_gicv3_its_translation_write(offset, data, size, attrs.requester_id); + switch (offset) { case GITS_TRANSLATER: if (s->ctlr & R_GITS_CTLR_ENABLED_MASK) { @@ -1107,6 +1112,7 @@ static MemTxResult gicv3_its_read(void *opaque, hwaddr offset, uint64_t *data, qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid guest read at offset " TARGET_FMT_plx "size %u\n", __func__, offset, size); + trace_gicv3_its_badread(offset, size); /* * The spec requires that reserved registers are RAZ/WI; * so use false returns from leaf functions as a way to @@ -1114,6 +1120,8 @@ static MemTxResult gicv3_its_read(void *opaque, hwaddr offset, uint64_t *data, * the caller, or we'll cause a spurious guest data abort. */ *data = 0; + } else { + trace_gicv3_its_read(offset, *data, size); } return MEMTX_OK; } @@ -1140,12 +1148,15 @@ static MemTxResult gicv3_its_write(void *opaque, hwaddr offset, uint64_t data, qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid guest write at offset " TARGET_FMT_plx "size %u\n", __func__, offset, size); + trace_gicv3_its_badwrite(offset, data, size); /* * The spec requires that reserved registers are RAZ/WI; * so use false returns from leaf functions as a way to * trigger the guest-error logging but don't return it to * the caller, or we'll cause a spurious guest data abort. */ + } else { + trace_gicv3_its_write(offset, data, size); } return MEMTX_OK; } diff --git a/hw/intc/trace-events b/hw/intc/trace-events index 9aba7e3a7a..b28cda4e08 100644 --- a/hw/intc/trace-events +++ b/hw/intc/trace-events @@ -169,6 +169,14 @@ gicv3_redist_badwrite(uint32_t cpu, uint64_t offset, uint64_t data, unsigned siz gicv3_redist_set_irq(uint32_t cpu, int irq, int level) "GICv3 redistributor 0x%x interrupt %d level changed to %d" gicv3_redist_send_sgi(uint32_t cpu, int irq) "GICv3 redistributor 0x%x pending SGI %d" +# arm_gicv3_its.c +gicv3_its_read(uint64_t offset, uint64_t data, unsigned size) "GICv3 ITS read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" +gicv3_its_badread(uint64_t offset, unsigned size) "GICv3 ITS read: offset 0x%" PRIx64 " size %u: error" +gicv3_its_write(uint64_t offset, uint64_t data, unsigned size) "GICv3 ITS write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" +gicv3_its_badwrite(uint64_t offset, uint64_t data, unsigned size) "GICv3 ITS write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u: error" +gicv3_its_translation_write(uint64_t offset, uint64_t data, unsigned size, uint32_t requester_id) "GICv3 ITS TRANSLATER write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u requester_id 0x%x" +gicv3_its_process_command(uint32_t rd_offset, uint8_t cmd) "GICv3 ITS: processing command at offset 0x%x: 0x%x" + # armv7m_nvic.c nvic_recompute_state(int vectpending, int vectpending_prio, int exception_prio) "NVIC state recomputed: vectpending %d vectpending_prio %d exception_prio %d" nvic_recompute_state_secure(int vectpending, bool vectpending_is_s_banked, int vectpending_prio, int exception_prio) "NVIC state recomputed: vectpending %d is_s_banked %d vectpending_prio %d exception_prio %d" From e5ff041f62fd01154dc91f9ac43ac3498b4689e1 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Sat, 22 Jan 2022 18:24:33 +0000 Subject: [PATCH 090/460] hw/intc/arm_gicv3: Initialise dma_as in GIC, not ITS In our implementation, all ITSes connected to a GIC share a single AddressSpace, which we keep in the GICv3State::dma_as field and initialized based on the GIC's 'sysmem' property. The right place to set it up by calling address_space_init() is therefore in the GIC's realize method, not the ITS's realize. This fixes a theoretical bug where QEMU hangs on startup if the board model creates two ITSes connected to the same GIC -- we would call address_space_init() twice on the same AddressSpace*, which creates an infinite loop in the QTAILQ that softmmu/memory.c uses to store its list of AddressSpaces and causes any subsequent attempt to iterate through that list to loop forever. There aren't any board models like that in the tree at the moment, though. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20220122182444.724087-4-peter.maydell@linaro.org --- hw/intc/arm_gicv3_common.c | 5 +++++ hw/intc/arm_gicv3_its.c | 3 --- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/hw/intc/arm_gicv3_common.c b/hw/intc/arm_gicv3_common.c index 9884d2e39b..579aa0cb9e 100644 --- a/hw/intc/arm_gicv3_common.c +++ b/hw/intc/arm_gicv3_common.c @@ -357,6 +357,11 @@ static void arm_gicv3_common_realize(DeviceState *dev, Error **errp) return; } + if (s->lpi_enable) { + address_space_init(&s->dma_as, s->dma, + "gicv3-its-sysmem"); + } + s->cpu = g_new0(GICv3CPUState, s->num_cpu); for (i = 0; i < s->num_cpu; i++) { diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c index 6d2549e64b..67f12d98af 100644 --- a/hw/intc/arm_gicv3_its.c +++ b/hw/intc/arm_gicv3_its.c @@ -1194,9 +1194,6 @@ static void gicv3_arm_its_realize(DeviceState *dev, Error **errp) gicv3_its_init_mmio(s, &gicv3_its_control_ops, &gicv3_its_translation_ops); - address_space_init(&s->gicv3->dma_as, s->gicv3->dma, - "gicv3-its-sysmem"); - /* set the ITS default features supported */ s->typer = FIELD_DP64(s->typer, GITS_TYPER, PHYSICAL, 1); s->typer = FIELD_DP64(s->typer, GITS_TYPER, ITT_ENTRY_SIZE, From 1e794a3be1687a8561c7188a3d3fc26f525cf487 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Sat, 22 Jan 2022 18:24:34 +0000 Subject: [PATCH 091/460] hw/intc/arm_gicv3_its: Don't clear GITS_CREADR when GITS_CTLR.ENABLED is set The current ITS code clears GITS_CREADR when GITS_CTLR.ENABLED is set. This is not correct -- guest code can validly clear ENABLED and then set it again and expect the ITS to continue processing where it left off. Remove the erroneous assignment. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20220122182444.724087-5-peter.maydell@linaro.org --- hw/intc/arm_gicv3_its.c | 1 - 1 file changed, 1 deletion(-) diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c index 67f12d98af..1763ba4a67 100644 --- a/hw/intc/arm_gicv3_its.c +++ b/hw/intc/arm_gicv3_its.c @@ -853,7 +853,6 @@ static bool its_writel(GICv3ITSState *s, hwaddr offset, s->ctlr |= R_GITS_CTLR_ENABLED_MASK; extract_table_params(s); extract_cmdq_params(s); - s->creadr = 0; process_cmdq(s); } else { s->ctlr &= ~R_GITS_CTLR_ENABLED_MASK; From 0cc38f359cbb50dd4f182b4ad3b7f7a17b1a4721 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Sat, 22 Jan 2022 18:24:35 +0000 Subject: [PATCH 092/460] hw/intc/arm_gicv3_its: Don't clear GITS_CWRITER on writes to GITS_CBASER The ITS specification says that when the guest writes to GITS_CBASER this causes GITS_CREADR to be cleared. However it does not have an equivalent clause for GITS_CWRITER. (This is because GITS_CREADR is read-only, but GITS_CWRITER is writable and the guest can initialize it.) Remove the code that clears GITS_CWRITER on GITS_CBASER writes. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20220122182444.724087-6-peter.maydell@linaro.org --- hw/intc/arm_gicv3_its.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c index 1763ba4a67..d9ff7b8849 100644 --- a/hw/intc/arm_gicv3_its.c +++ b/hw/intc/arm_gicv3_its.c @@ -866,7 +866,6 @@ static bool its_writel(GICv3ITSState *s, hwaddr offset, if (!(s->ctlr & R_GITS_CTLR_ENABLED_MASK)) { s->cbaser = deposit64(s->cbaser, 0, 32, value); s->creadr = 0; - s->cwriter = s->creadr; } break; case GITS_CBASER + 4: @@ -877,7 +876,6 @@ static bool its_writel(GICv3ITSState *s, hwaddr offset, if (!(s->ctlr & R_GITS_CTLR_ENABLED_MASK)) { s->cbaser = deposit64(s->cbaser, 32, 32, value); s->creadr = 0; - s->cwriter = s->creadr; } break; case GITS_CWRITER: @@ -1027,7 +1025,6 @@ static bool its_writell(GICv3ITSState *s, hwaddr offset, if (!(s->ctlr & R_GITS_CTLR_ENABLED_MASK)) { s->cbaser = value; s->creadr = 0; - s->cwriter = s->creadr; } break; case GITS_CWRITER: From 703090770c19dad32f42a8bc27393ed01b7bc42f Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Sat, 22 Jan 2022 18:24:36 +0000 Subject: [PATCH 093/460] hw/intc/arm_gicv3: Honour GICD_CTLR.EnableGrp1NS for LPIs The GICD_CTLR distributor register has enable bits which control whether the different interrupt groups (Group 0, Non-secure Group 1 and Secure Group 1) are forwarded to the CPU. We get this right for traditional interrupts, but forgot to account for it when adding LPIs. LPIs are always Group 1 NS and if the EnableGrp1NS bit is not set we must not forward them to the CPU. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20220122182444.724087-7-peter.maydell@linaro.org --- hw/intc/arm_gicv3.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/intc/arm_gicv3.c b/hw/intc/arm_gicv3.c index 715df5421d..6d3c8ee231 100644 --- a/hw/intc/arm_gicv3.c +++ b/hw/intc/arm_gicv3.c @@ -166,6 +166,7 @@ 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)) { cs->hppi.irq = cs->hpplpi.irq; From 714d8bde0423b38117bfebd88e2f09a01a0efbfd Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Sat, 22 Jan 2022 18:24:37 +0000 Subject: [PATCH 094/460] hw/intc/arm_gicv3_its: Sort ITS command list into numeric order MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The list of #defines for the ITS command packet numbers is neither in alphabetical nor numeric order. Sort it into numeric order. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-id: 20220122182444.724087-8-peter.maydell@linaro.org --- hw/intc/gicv3_internal.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/hw/intc/gicv3_internal.h b/hw/intc/gicv3_internal.h index 1eeb99035d..5394266aaf 100644 --- a/hw/intc/gicv3_internal.h +++ b/hw/intc/gicv3_internal.h @@ -314,16 +314,16 @@ FIELD(GITS_TYPER, CIL, 36, 1) #define CMD_MASK 0xff /* ITS Commands */ -#define GITS_CMD_CLEAR 0x04 -#define GITS_CMD_DISCARD 0x0F #define GITS_CMD_INT 0x03 -#define GITS_CMD_MAPC 0x09 +#define GITS_CMD_CLEAR 0x04 +#define GITS_CMD_SYNC 0x05 #define GITS_CMD_MAPD 0x08 -#define GITS_CMD_MAPI 0x0B +#define GITS_CMD_MAPC 0x09 #define GITS_CMD_MAPTI 0x0A +#define GITS_CMD_MAPI 0x0B #define GITS_CMD_INV 0x0C #define GITS_CMD_INVALL 0x0D -#define GITS_CMD_SYNC 0x05 +#define GITS_CMD_DISCARD 0x0F /* MAPC command fields */ #define ICID_LENGTH 16 From d7d19c0aeb7d657c76c88913744ff53fc7e24c23 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Sat, 22 Jan 2022 18:24:38 +0000 Subject: [PATCH 095/460] hw/intc/arm_gicv3_redist: Remove unnecessary zero checks The ITS-related parts of the redistributor code make some checks for whether registers like GICR_PROPBASER and GICR_PENDBASER are zero. There is no requirement in the specification for treating zeroes in these address registers specially -- they contain guest physical addresses and it is entirely valid (if unusual) for the guest to choose to put the tables they address at guest physical address zero. We use these values only to calculate guest addresses, and attempts by the guest to use a bad address will be handled by the address_space_* functions which we use to do the loads and stores. Remove the unnecessary checks. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20220122182444.724087-9-peter.maydell@linaro.org --- hw/intc/arm_gicv3_redist.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/hw/intc/arm_gicv3_redist.c b/hw/intc/arm_gicv3_redist.c index 99b11ca5ee..d81d8e5f07 100644 --- a/hw/intc/arm_gicv3_redist.c +++ b/hw/intc/arm_gicv3_redist.c @@ -591,8 +591,7 @@ void gicv3_redist_update_lpi_only(GICv3CPUState *cs) idbits = MIN(FIELD_EX64(cs->gicr_propbaser, GICR_PROPBASER, IDBITS), GICD_TYPER_IDBITS); - if (!(cs->gicr_ctlr & GICR_CTLR_ENABLE_LPIS) || !cs->gicr_propbaser || - !cs->gicr_pendbaser) { + if (!(cs->gicr_ctlr & GICR_CTLR_ENABLE_LPIS)) { return; } @@ -673,9 +672,8 @@ void gicv3_redist_process_lpi(GICv3CPUState *cs, int irq, int level) idbits = MIN(FIELD_EX64(cs->gicr_propbaser, GICR_PROPBASER, IDBITS), GICD_TYPER_IDBITS); - if (!(cs->gicr_ctlr & GICR_CTLR_ENABLE_LPIS) || !cs->gicr_propbaser || - !cs->gicr_pendbaser || (irq > (1ULL << (idbits + 1)) - 1) || - irq < GICV3_LPI_INTID_START) { + if (!(cs->gicr_ctlr & GICR_CTLR_ENABLE_LPIS) || + (irq > (1ULL << (idbits + 1)) - 1) || irq < GICV3_LPI_INTID_START) { return; } From 1611956bce06b0721ea949e24c089ef22967672a Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Sat, 22 Jan 2022 18:24:39 +0000 Subject: [PATCH 096/460] hw/intc/arm_gicv3: Set GICR_CTLR.CES if LPIs are supported The GICR_CTLR.CES bit is a read-only bit which is set to 1 to indicate that the GICR_CTLR.EnableLPIs bit can be written to 0 to disable LPIs (as opposed to allowing LPIs to be enabled but not subsequently disabled). Our implementation permits this, so advertise it by setting CES to 1. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20220122182444.724087-10-peter.maydell@linaro.org --- hw/intc/arm_gicv3_common.c | 4 ++++ hw/intc/gicv3_internal.h | 1 + 2 files changed, 5 insertions(+) diff --git a/hw/intc/arm_gicv3_common.c b/hw/intc/arm_gicv3_common.c index 579aa0cb9e..4ca5ae9bc5 100644 --- a/hw/intc/arm_gicv3_common.c +++ b/hw/intc/arm_gicv3_common.c @@ -429,6 +429,10 @@ static void arm_gicv3_common_reset(DeviceState *dev) cs->level = 0; cs->gicr_ctlr = 0; + if (s->lpi_enable) { + /* Our implementation supports clearing GICR_CTLR.EnableLPIs */ + cs->gicr_ctlr |= GICR_CTLR_CES; + } cs->gicr_statusr[GICV3_S] = 0; cs->gicr_statusr[GICV3_NS] = 0; cs->gicr_waker = GICR_WAKER_ProcessorSleep | GICR_WAKER_ChildrenAsleep; diff --git a/hw/intc/gicv3_internal.h b/hw/intc/gicv3_internal.h index 5394266aaf..a316f6c58a 100644 --- a/hw/intc/gicv3_internal.h +++ b/hw/intc/gicv3_internal.h @@ -110,6 +110,7 @@ #define GICR_NSACR (GICR_SGI_OFFSET + 0x0E00) #define GICR_CTLR_ENABLE_LPIS (1U << 0) +#define GICR_CTLR_CES (1U << 1) #define GICR_CTLR_RWP (1U << 3) #define GICR_CTLR_DPG0 (1U << 24) #define GICR_CTLR_DPG1NS (1U << 25) From 7e062b98a2541ca9a632160aadbe8574c8bdce24 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Sat, 22 Jan 2022 18:24:40 +0000 Subject: [PATCH 097/460] hw/intc/arm_gicv3_its: Provide read accessor for translation_ops MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The MemoryRegionOps gicv3_its_translation_ops currently provides only a .write_with_attrs function, because the only register in this region is the write-only GITS_TRANSLATER. However, if you don't provide a read function and the guest tries reading from this memory region, QEMU will crash because memory_region_read_with_attrs_accessor() calls a NULL pointer. Add a read function which always returns 0, to cover both bogus attempts to read GITS_TRANSLATER and also reads from the rest of the region, which is documented to be reserved, RES0. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-id: 20220122182444.724087-11-peter.maydell@linaro.org --- hw/intc/arm_gicv3_its.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c index d9ff7b8849..b17f263126 100644 --- a/hw/intc/arm_gicv3_its.c +++ b/hw/intc/arm_gicv3_its.c @@ -813,6 +813,18 @@ static void extract_cmdq_params(GICv3ITSState *s) } } +static MemTxResult gicv3_its_translation_read(void *opaque, hwaddr offset, + uint64_t *data, unsigned size, + MemTxAttrs attrs) +{ + /* + * GITS_TRANSLATER is write-only, and all other addresses + * in the interrupt translation space frame are RES0. + */ + *data = 0; + return MEMTX_OK; +} + static MemTxResult gicv3_its_translation_write(void *opaque, hwaddr offset, uint64_t data, unsigned size, MemTxAttrs attrs) @@ -1168,6 +1180,7 @@ static const MemoryRegionOps gicv3_its_control_ops = { }; static const MemoryRegionOps gicv3_its_translation_ops = { + .read_with_attrs = gicv3_its_translation_read, .write_with_attrs = gicv3_its_translation_write, .valid.min_access_size = 2, .valid.max_access_size = 4, From 0ffe88e6919d210c806078aaf0c34911554f1438 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Sat, 22 Jan 2022 18:24:41 +0000 Subject: [PATCH 098/460] hw/intc/arm_gicv3_its: Make GITS_BASER RAZ/WI for unimplemented registers The ITS has a bank of 8 GITS_BASER registers, which allow the guest to specify the base address of various data tables. Each register has a read-only type field indicating which table it is for and a read-write field where the guest can write in the base address (among other things). We currently allow the guest to write the writeable fields for all eight registers, even if the type field is 0 indicating "Unimplemented". This means the guest can provoke QEMU into asserting by writing an address into one of these unimplemented base registers, which bypasses the "if (!value) continue" check in extract_table_params() and lets us hit the assertion that the type field is one of the permitted table types. Prevent the assertion by not allowing the guest to write to the unimplemented base registers. This means their value will remain 0 and extract_table_params() will ignore them. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20220122182444.724087-12-peter.maydell@linaro.org --- hw/intc/arm_gicv3_its.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c index b17f263126..237198845d 100644 --- a/hw/intc/arm_gicv3_its.c +++ b/hw/intc/arm_gicv3_its.c @@ -929,6 +929,10 @@ static bool its_writel(GICv3ITSState *s, hwaddr offset, if (!(s->ctlr & R_GITS_CTLR_ENABLED_MASK)) { index = (offset - GITS_BASER) / 8; + if (s->baser[index] == 0) { + /* Unimplemented GITS_BASERn: RAZ/WI */ + break; + } if (offset & 7) { value <<= 32; value &= ~GITS_BASER_RO_MASK; @@ -1025,6 +1029,10 @@ static bool its_writell(GICv3ITSState *s, hwaddr offset, */ if (!(s->ctlr & R_GITS_CTLR_ENABLED_MASK)) { index = (offset - GITS_BASER) / 8; + if (s->baser[index] == 0) { + /* Unimplemented GITS_BASERn: RAZ/WI */ + break; + } s->baser[index] &= GITS_BASER_RO_MASK; s->baser[index] |= (value & ~GITS_BASER_RO_MASK); } From 8b8bb0146b5383188e045ab75a53a0e179614cad Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Sat, 22 Jan 2022 18:24:42 +0000 Subject: [PATCH 099/460] hw/intc/arm_gicv3_its: Check table bounds against correct limit Currently when we fill in a TableDesc based on the value the guest has written to the GITS_BASER register, we calculate both: * num_entries : the number of entries in the table, constrained by the amount of memory the guest has given it * num_ids : the number of IDs we support for this table, constrained by the implementation choices and the architecture (eg DeviceIDs are 16 bits, so num_ids is 1 << 16) When validating ITS commands, however, we check only num_ids, thus allowing a broken guest to specify table entries that index off the end of it. This will only corrupt guest memory, but the ITS is supposed to reject such commands as invalid. Instead of calculating both num_entries and num_ids, set num_entries to the minimum of the two limits, and check that. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20220122182444.724087-13-peter.maydell@linaro.org --- hw/intc/arm_gicv3_its.c | 18 +++++++++--------- include/hw/intc/arm_gicv3_its_common.h | 1 - 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c index 237198845d..3f2ead4536 100644 --- a/hw/intc/arm_gicv3_its.c +++ b/hw/intc/arm_gicv3_its.c @@ -256,10 +256,10 @@ static ItsCmdResult process_its_cmd(GICv3ITSState *s, uint64_t value, eventid = (value & EVENTID_MASK); - if (devid >= s->dt.num_ids) { + if (devid >= s->dt.num_entries) { qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid command attributes: devid %d>=%d", - __func__, devid, s->dt.num_ids); + __func__, devid, s->dt.num_entries); return CMD_CONTINUE; } @@ -300,7 +300,7 @@ static ItsCmdResult process_its_cmd(GICv3ITSState *s, uint64_t value, return CMD_CONTINUE; } - if (icid >= s->ct.num_ids) { + if (icid >= s->ct.num_entries) { qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid ICID 0x%x in ITE (table corrupted?)\n", __func__, icid); @@ -384,10 +384,10 @@ static ItsCmdResult process_mapti(GICv3ITSState *s, uint64_t value, icid = value & ICID_MASK; - if (devid >= s->dt.num_ids) { + if (devid >= s->dt.num_entries) { qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid command attributes: devid %d>=%d", - __func__, devid, s->dt.num_ids); + __func__, devid, s->dt.num_entries); return CMD_CONTINUE; } @@ -400,7 +400,7 @@ static ItsCmdResult process_mapti(GICv3ITSState *s, uint64_t value, num_eventids = 1ULL << (FIELD_EX64(dte, DTE, SIZE) + 1); num_intids = 1ULL << (GICD_TYPER_IDBITS + 1); - if ((icid >= s->ct.num_ids) + if ((icid >= s->ct.num_entries) || !dte_valid || (eventid >= num_eventids) || (((pIntid < GICV3_LPI_INTID_START) || (pIntid >= num_intids)) && (pIntid != INTID_SPURIOUS))) { @@ -485,7 +485,7 @@ static ItsCmdResult process_mapc(GICv3ITSState *s, uint32_t offset) valid = (value & CMD_FIELD_VALID_MASK); - if ((icid >= s->ct.num_ids) || (rdbase >= s->gicv3->num_cpu)) { + if ((icid >= s->ct.num_entries) || (rdbase >= s->gicv3->num_cpu)) { qemu_log_mask(LOG_GUEST_ERROR, "ITS MAPC: invalid collection table attributes " "icid %d rdbase %" PRIu64 "\n", icid, rdbase); @@ -566,7 +566,7 @@ static ItsCmdResult process_mapd(GICv3ITSState *s, uint64_t value, valid = (value & CMD_FIELD_VALID_MASK); - if ((devid >= s->dt.num_ids) || + if ((devid >= s->dt.num_entries) || (size > FIELD_EX64(s->typer, GITS_TYPER, IDBITS))) { qemu_log_mask(LOG_GUEST_ERROR, "ITS MAPD: invalid device table attributes " @@ -791,7 +791,7 @@ static void extract_table_params(GICv3ITSState *s) L1TABLE_ENTRY_SIZE) * (page_sz / td->entry_sz)); } - td->num_ids = 1ULL << idbits; + td->num_entries = MIN(td->num_entries, 1ULL << idbits); } } diff --git a/include/hw/intc/arm_gicv3_its_common.h b/include/hw/intc/arm_gicv3_its_common.h index b32c697207..3e2ad2dff6 100644 --- a/include/hw/intc/arm_gicv3_its_common.h +++ b/include/hw/intc/arm_gicv3_its_common.h @@ -47,7 +47,6 @@ typedef struct { uint16_t entry_sz; uint32_t page_sz; uint32_t num_entries; - uint32_t num_ids; uint64_t base_addr; } TableDesc; From f6d1d9b4074d64de92f3ab4dfa50dc19548fdfd7 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Sat, 22 Jan 2022 18:24:43 +0000 Subject: [PATCH 100/460] hw/intc/arm_gicv3_its: Implement MOVALL Implement the ITS MOVALL command, which takes all the pending interrupts on a source redistributor and makes the not-pending on that source redistributor and pending on a destination redistributor. This is a GICv3 ITS command which we forgot to implement. (It is not used by Linux guests.) Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20220122182444.724087-14-peter.maydell@linaro.org --- hw/intc/arm_gicv3_its.c | 55 ++++++++++++++++++++++++++++++++++++++ hw/intc/arm_gicv3_redist.c | 54 +++++++++++++++++++++++++++++++++++++ hw/intc/gicv3_internal.h | 16 +++++++++++ 3 files changed, 125 insertions(+) diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c index 3f2ead4536..ebc0403b3c 100644 --- a/hw/intc/arm_gicv3_its.c +++ b/hw/intc/arm_gicv3_its.c @@ -582,6 +582,58 @@ static ItsCmdResult process_mapd(GICv3ITSState *s, uint64_t value, return update_dte(s, devid, valid, size, itt_addr) ? CMD_CONTINUE : CMD_STALL; } +static ItsCmdResult process_movall(GICv3ITSState *s, uint64_t value, + uint32_t offset) +{ + AddressSpace *as = &s->gicv3->dma_as; + MemTxResult res = MEMTX_OK; + uint64_t rd1, rd2; + + /* No fields in dwords 0 or 1 */ + offset += NUM_BYTES_IN_DW; + offset += NUM_BYTES_IN_DW; + value = address_space_ldq_le(as, s->cq.base_addr + offset, + MEMTXATTRS_UNSPECIFIED, &res); + if (res != MEMTX_OK) { + return CMD_STALL; + } + + rd1 = FIELD_EX64(value, MOVALL_2, RDBASE1); + if (rd1 >= s->gicv3->num_cpu) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: RDBASE1 %" PRId64 + " out of range (must be less than %d)\n", + __func__, rd1, s->gicv3->num_cpu); + return CMD_CONTINUE; + } + + offset += NUM_BYTES_IN_DW; + value = address_space_ldq_le(as, s->cq.base_addr + offset, + MEMTXATTRS_UNSPECIFIED, &res); + if (res != MEMTX_OK) { + return CMD_STALL; + } + + rd2 = FIELD_EX64(value, MOVALL_3, RDBASE2); + if (rd2 >= s->gicv3->num_cpu) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: RDBASE2 %" PRId64 + " out of range (must be less than %d)\n", + __func__, rd2, s->gicv3->num_cpu); + return CMD_CONTINUE; + } + + if (rd1 == rd2) { + /* Move to same target must succeed as a no-op */ + return CMD_CONTINUE; + } + + /* Move all pending LPIs from redistributor 1 to redistributor 2 */ + gicv3_redist_movall_lpis(&s->gicv3->cpu[rd1], &s->gicv3->cpu[rd2]); + + return CMD_CONTINUE; +} + /* * Current implementation blocks until all * commands are processed @@ -679,6 +731,9 @@ static void process_cmdq(GICv3ITSState *s) gicv3_redist_update_lpi(&s->gicv3->cpu[i]); } break; + case GITS_CMD_MOVALL: + result = process_movall(s, data, cq_offset); + break; default: break; } diff --git a/hw/intc/arm_gicv3_redist.c b/hw/intc/arm_gicv3_redist.c index d81d8e5f07..d1645ba22c 100644 --- a/hw/intc/arm_gicv3_redist.c +++ b/hw/intc/arm_gicv3_redist.c @@ -681,6 +681,60 @@ void gicv3_redist_process_lpi(GICv3CPUState *cs, int irq, int level) gicv3_redist_lpi_pending(cs, irq, level); } +void gicv3_redist_movall_lpis(GICv3CPUState *src, GICv3CPUState *dest) +{ + /* + * We must move all pending LPIs from the source redistributor + * to the destination. That is, for every pending LPI X on + * src, we must set it not-pending on src and pending on dest. + * LPIs that are already pending on dest are not cleared. + * + * If LPIs are disabled on dest this is CONSTRAINED UNPREDICTABLE: + * we choose to NOP. If LPIs are disabled on source there's nothing + * to be transferred anyway. + */ + AddressSpace *as = &src->gic->dma_as; + uint64_t idbits; + uint32_t pendt_size; + uint64_t src_baddr, dest_baddr; + int i; + + if (!(src->gicr_ctlr & GICR_CTLR_ENABLE_LPIS) || + !(dest->gicr_ctlr & GICR_CTLR_ENABLE_LPIS)) { + return; + } + + idbits = MIN(FIELD_EX64(src->gicr_propbaser, GICR_PROPBASER, IDBITS), + GICD_TYPER_IDBITS); + idbits = MIN(FIELD_EX64(dest->gicr_propbaser, GICR_PROPBASER, IDBITS), + idbits); + + pendt_size = 1ULL << (idbits + 1); + src_baddr = src->gicr_pendbaser & R_GICR_PENDBASER_PHYADDR_MASK; + dest_baddr = dest->gicr_pendbaser & R_GICR_PENDBASER_PHYADDR_MASK; + + for (i = GICV3_LPI_INTID_START / 8; i < pendt_size / 8; i++) { + uint8_t src_pend, dest_pend; + + address_space_read(as, src_baddr + i, MEMTXATTRS_UNSPECIFIED, + &src_pend, sizeof(src_pend)); + if (!src_pend) { + continue; + } + address_space_read(as, dest_baddr + i, MEMTXATTRS_UNSPECIFIED, + &dest_pend, sizeof(dest_pend)); + dest_pend |= src_pend; + src_pend = 0; + address_space_write(as, src_baddr + i, MEMTXATTRS_UNSPECIFIED, + &src_pend, sizeof(src_pend)); + address_space_write(as, dest_baddr + i, MEMTXATTRS_UNSPECIFIED, + &dest_pend, sizeof(dest_pend)); + } + + gicv3_redist_update_lpi(src); + gicv3_redist_update_lpi(dest); +} + void gicv3_redist_set_irq(GICv3CPUState *cs, int irq, int level) { /* Update redistributor state for a change in an external PPI input line */ diff --git a/hw/intc/gicv3_internal.h b/hw/intc/gicv3_internal.h index a316f6c58a..da45975d92 100644 --- a/hw/intc/gicv3_internal.h +++ b/hw/intc/gicv3_internal.h @@ -324,6 +324,7 @@ FIELD(GITS_TYPER, CIL, 36, 1) #define GITS_CMD_MAPI 0x0B #define GITS_CMD_INV 0x0C #define GITS_CMD_INVALL 0x0D +#define GITS_CMD_MOVALL 0x0E #define GITS_CMD_DISCARD 0x0F /* MAPC command fields */ @@ -355,6 +356,10 @@ FIELD(MAPC, RDBASE, 16, 32) #define L2_TABLE_VALID_MASK CMD_FIELD_VALID_MASK #define TABLE_ENTRY_VALID_MASK (1ULL << 0) +/* MOVALL command fields */ +FIELD(MOVALL_2, RDBASE1, 16, 36) +FIELD(MOVALL_3, RDBASE2, 16, 36) + /* * 12 bytes Interrupt translation Table Entry size * as per Table 5.3 in GICv3 spec @@ -497,6 +502,17 @@ void gicv3_redist_update_lpi(GICv3CPUState *cs); * an incoming migration has loaded new state. */ void gicv3_redist_update_lpi_only(GICv3CPUState *cs); +/** + * gicv3_redist_movall_lpis: + * @src: source redistributor + * @dest: destination redistributor + * + * Scan the LPI pending table for @src, and for each pending LPI there + * mark it as not-pending for @src and pending for @dest, as required + * by the ITS MOVALL command. + */ +void gicv3_redist_movall_lpis(GICv3CPUState *src, GICv3CPUState *dest); + void gicv3_redist_send_sgi(GICv3CPUState *cs, int grp, int irq, bool ns); void gicv3_init_cpuif(GICv3State *s); From 961b4912c1330aaf11a354c9d8f5c63e1ba0ae3b Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Sat, 22 Jan 2022 18:24:44 +0000 Subject: [PATCH 101/460] hw/intc/arm_gicv3_its: Implement MOVI Implement the ITS MOVI command. This command specifies a (physical) LPI by DeviceID and EventID and provides a new ICID for it. The ITS must find the interrupt translation table entry for the LPI, which will tell it the old ICID. It then moves the pending state of the LPI from the old redistributor to the new one and updates the ICID field in the translation table entry. This is another GICv3 ITS command that we forgot to implement. Linux does use this one, but only if the guest powers off one of its CPUs. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20220122182444.724087-15-peter.maydell@linaro.org --- hw/intc/arm_gicv3_its.c | 146 +++++++++++++++++++++++++++++++++++++ hw/intc/arm_gicv3_redist.c | 53 ++++++++++++++ hw/intc/gicv3_internal.h | 16 ++++ 3 files changed, 215 insertions(+) diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c index ebc0403b3c..51d9be4ae6 100644 --- a/hw/intc/arm_gicv3_its.c +++ b/hw/intc/arm_gicv3_its.c @@ -634,6 +634,149 @@ static ItsCmdResult process_movall(GICv3ITSState *s, uint64_t value, return CMD_CONTINUE; } +static ItsCmdResult process_movi(GICv3ITSState *s, uint64_t value, + uint32_t offset) +{ + AddressSpace *as = &s->gicv3->dma_as; + MemTxResult res = MEMTX_OK; + uint32_t devid, eventid, intid; + uint16_t old_icid, new_icid; + uint64_t old_cte, new_cte; + uint64_t old_rdbase, new_rdbase; + uint64_t dte; + bool dte_valid, ite_valid, cte_valid; + uint64_t num_eventids; + IteEntry ite = {}; + + devid = FIELD_EX64(value, MOVI_0, DEVICEID); + + offset += NUM_BYTES_IN_DW; + value = address_space_ldq_le(as, s->cq.base_addr + offset, + MEMTXATTRS_UNSPECIFIED, &res); + if (res != MEMTX_OK) { + return CMD_STALL; + } + eventid = FIELD_EX64(value, MOVI_1, EVENTID); + + offset += NUM_BYTES_IN_DW; + value = address_space_ldq_le(as, s->cq.base_addr + offset, + MEMTXATTRS_UNSPECIFIED, &res); + if (res != MEMTX_OK) { + return CMD_STALL; + } + new_icid = FIELD_EX64(value, MOVI_2, ICID); + + if (devid >= s->dt.num_entries) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: invalid command attributes: devid %d>=%d", + __func__, devid, s->dt.num_entries); + return CMD_CONTINUE; + } + dte = get_dte(s, devid, &res); + if (res != MEMTX_OK) { + return CMD_STALL; + } + + dte_valid = FIELD_EX64(dte, DTE, VALID); + if (!dte_valid) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: invalid command attributes: " + "invalid dte: %"PRIx64" for %d\n", + __func__, dte, devid); + return CMD_CONTINUE; + } + + num_eventids = 1ULL << (FIELD_EX64(dte, DTE, SIZE) + 1); + if (eventid >= num_eventids) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: invalid command attributes: eventid %d >= %" + PRId64 "\n", + __func__, eventid, num_eventids); + return CMD_CONTINUE; + } + + ite_valid = get_ite(s, eventid, dte, &old_icid, &intid, &res); + if (res != MEMTX_OK) { + return CMD_STALL; + } + + if (!ite_valid) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: invalid command attributes: invalid ITE\n", + __func__); + return CMD_CONTINUE; + } + + if (old_icid >= s->ct.num_entries) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: invalid ICID 0x%x in ITE (table corrupted?)\n", + __func__, old_icid); + return CMD_CONTINUE; + } + + if (new_icid >= s->ct.num_entries) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: invalid command attributes: ICID 0x%x\n", + __func__, new_icid); + return CMD_CONTINUE; + } + + cte_valid = get_cte(s, old_icid, &old_cte, &res); + if (res != MEMTX_OK) { + return CMD_STALL; + } + if (!cte_valid) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: invalid command attributes: " + "invalid cte: %"PRIx64"\n", + __func__, old_cte); + return CMD_CONTINUE; + } + + cte_valid = get_cte(s, new_icid, &new_cte, &res); + if (res != MEMTX_OK) { + return CMD_STALL; + } + if (!cte_valid) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: invalid command attributes: " + "invalid cte: %"PRIx64"\n", + __func__, new_cte); + return CMD_CONTINUE; + } + + old_rdbase = FIELD_EX64(old_cte, CTE, RDBASE); + if (old_rdbase >= s->gicv3->num_cpu) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: CTE has invalid rdbase 0x%"PRIx64"\n", + __func__, old_rdbase); + return CMD_CONTINUE; + } + + new_rdbase = FIELD_EX64(new_cte, CTE, RDBASE); + if (new_rdbase >= s->gicv3->num_cpu) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: CTE has invalid rdbase 0x%"PRIx64"\n", + __func__, new_rdbase); + return CMD_CONTINUE; + } + + if (old_rdbase != new_rdbase) { + /* Move the LPI from the old redistributor to the new one */ + gicv3_redist_mov_lpi(&s->gicv3->cpu[old_rdbase], + &s->gicv3->cpu[new_rdbase], + intid); + } + + /* Update the ICID field in the interrupt translation table entry */ + ite.itel = FIELD_DP64(ite.itel, ITE_L, VALID, 1); + ite.itel = FIELD_DP64(ite.itel, ITE_L, INTTYPE, ITE_INTTYPE_PHYSICAL); + ite.itel = FIELD_DP64(ite.itel, ITE_L, INTID, intid); + ite.itel = FIELD_DP64(ite.itel, ITE_L, DOORBELL, INTID_SPURIOUS); + ite.iteh = FIELD_DP32(ite.iteh, ITE_H, ICID, new_icid); + return update_ite(s, eventid, dte, ite) ? CMD_CONTINUE : CMD_STALL; +} + /* * Current implementation blocks until all * commands are processed @@ -731,6 +874,9 @@ static void process_cmdq(GICv3ITSState *s) gicv3_redist_update_lpi(&s->gicv3->cpu[i]); } break; + case GITS_CMD_MOVI: + result = process_movi(s, data, cq_offset); + break; case GITS_CMD_MOVALL: result = process_movall(s, data, cq_offset); break; diff --git a/hw/intc/arm_gicv3_redist.c b/hw/intc/arm_gicv3_redist.c index d1645ba22c..412a04f59c 100644 --- a/hw/intc/arm_gicv3_redist.c +++ b/hw/intc/arm_gicv3_redist.c @@ -681,6 +681,59 @@ void gicv3_redist_process_lpi(GICv3CPUState *cs, int irq, int level) gicv3_redist_lpi_pending(cs, irq, level); } +void gicv3_redist_mov_lpi(GICv3CPUState *src, GICv3CPUState *dest, int irq) +{ + /* + * Move the specified LPI's pending state from the source redistributor + * to the destination. + * + * If LPIs are disabled on dest this is CONSTRAINED UNPREDICTABLE: + * we choose to NOP. If LPIs are disabled on source there's nothing + * to be transferred anyway. + */ + AddressSpace *as = &src->gic->dma_as; + uint64_t idbits; + uint32_t pendt_size; + uint64_t src_baddr; + uint8_t src_pend; + + if (!(src->gicr_ctlr & GICR_CTLR_ENABLE_LPIS) || + !(dest->gicr_ctlr & GICR_CTLR_ENABLE_LPIS)) { + return; + } + + idbits = MIN(FIELD_EX64(src->gicr_propbaser, GICR_PROPBASER, IDBITS), + GICD_TYPER_IDBITS); + idbits = MIN(FIELD_EX64(dest->gicr_propbaser, GICR_PROPBASER, IDBITS), + idbits); + + pendt_size = 1ULL << (idbits + 1); + if ((irq / 8) >= pendt_size) { + return; + } + + src_baddr = src->gicr_pendbaser & R_GICR_PENDBASER_PHYADDR_MASK; + + address_space_read(as, src_baddr + (irq / 8), + MEMTXATTRS_UNSPECIFIED, &src_pend, sizeof(src_pend)); + if (!extract32(src_pend, irq % 8, 1)) { + /* Not pending on source, nothing to do */ + return; + } + src_pend &= ~(1 << (irq % 8)); + address_space_write(as, src_baddr + (irq / 8), + MEMTXATTRS_UNSPECIFIED, &src_pend, sizeof(src_pend)); + if (irq == src->hpplpi.irq) { + /* + * We just made this LPI not-pending so only need to update + * if it was previously the highest priority pending LPI + */ + gicv3_redist_update_lpi(src); + } + /* Mark it pending on the destination */ + gicv3_redist_lpi_pending(dest, irq, 1); +} + void gicv3_redist_movall_lpis(GICv3CPUState *src, GICv3CPUState *dest) { /* diff --git a/hw/intc/gicv3_internal.h b/hw/intc/gicv3_internal.h index da45975d92..b1af26df9f 100644 --- a/hw/intc/gicv3_internal.h +++ b/hw/intc/gicv3_internal.h @@ -315,6 +315,7 @@ FIELD(GITS_TYPER, CIL, 36, 1) #define CMD_MASK 0xff /* ITS Commands */ +#define GITS_CMD_MOVI 0x01 #define GITS_CMD_INT 0x03 #define GITS_CMD_CLEAR 0x04 #define GITS_CMD_SYNC 0x05 @@ -360,6 +361,11 @@ FIELD(MAPC, RDBASE, 16, 32) FIELD(MOVALL_2, RDBASE1, 16, 36) FIELD(MOVALL_3, RDBASE2, 16, 36) +/* MOVI command fields */ +FIELD(MOVI_0, DEVICEID, 32, 32) +FIELD(MOVI_1, EVENTID, 0, 32) +FIELD(MOVI_2, ICID, 0, 16) + /* * 12 bytes Interrupt translation Table Entry size * as per Table 5.3 in GICv3 spec @@ -502,6 +508,16 @@ void gicv3_redist_update_lpi(GICv3CPUState *cs); * an incoming migration has loaded new state. */ void gicv3_redist_update_lpi_only(GICv3CPUState *cs); +/** + * gicv3_redist_mov_lpi: + * @src: source redistributor + * @dest: destination redistributor + * @irq: LPI to update + * + * Move the pending state of the specified LPI from @src to @dest, + * as required by the ITS MOVI command. + */ +void gicv3_redist_mov_lpi(GICv3CPUState *src, GICv3CPUState *dest, int irq); /** * gicv3_redist_movall_lpis: * @src: source redistributor From 08048cbd5e7dc0a0359ccb8c7968e4d011174801 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 26 Jan 2022 09:35:20 +0100 Subject: [PATCH 102/460] hw/arm: ast2600: Fix address mapping of second SPI controller MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Address should be 0x1E631000 and not 0x1E641000 as initially introduced. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/838 Fixes: f25c0ae1079d ("aspeed/soc: Add AST2600 support") Suggested-by: Troy Lee Signed-off-by: Cédric Le Goater Reviewed-by: Philippe Mathieu-Daudé Message-id: 20220126083520.4135713-1-clg@kaod.org Signed-off-by: Peter Maydell --- hw/arm/aspeed_ast2600.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/arm/aspeed_ast2600.c b/hw/arm/aspeed_ast2600.c index 8f37bdb1d8..12f6edc081 100644 --- a/hw/arm/aspeed_ast2600.c +++ b/hw/arm/aspeed_ast2600.c @@ -29,7 +29,7 @@ static const hwaddr aspeed_soc_ast2600_memmap[] = { [ASPEED_DEV_PWM] = 0x1E610000, [ASPEED_DEV_FMC] = 0x1E620000, [ASPEED_DEV_SPI1] = 0x1E630000, - [ASPEED_DEV_SPI2] = 0x1E641000, + [ASPEED_DEV_SPI2] = 0x1E631000, [ASPEED_DEV_EHCI1] = 0x1E6A1000, [ASPEED_DEV_EHCI2] = 0x1E6A3000, [ASPEED_DEV_MII1] = 0x1E650000, From 2c023d3675a3ffb54fc30504dcd715bc6f6e234f Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 17 Jan 2022 13:19:53 +0000 Subject: [PATCH 103/460] target/arm: Use correct entrypoint for SVC taken from Hyp to Hyp The exception caused by an SVC instruction may be taken to AArch32 Hyp mode for two reasons: * HCR.TGE indicates that exceptions from EL0 should trap to EL2 * we were already in Hyp mode The entrypoint in the vector table to be used differs in these two cases: for an exception routed to Hyp mode from EL0, we enter at the common 0x14 "hyp trap" entrypoint. For SVC from Hyp mode to Hyp mode, we enter at the 0x08 (svc/hvc trap) entrypoint. In the v8A Arm ARM pseudocode this is done in AArch32.TakeSVCException. QEMU incorrectly routed both of these exceptions to the 0x14 entrypoint. Correct the entrypoint for SVC from Hyp to Hyp by making use of the existing logic which handles "normal entrypoint for Hyp-to-Hyp, otherwise 0x14" for traps like UNDEF and data/prefetch aborts (reproduced here since it's outside the visible context in the diff for this commit): if (arm_current_el(env) != 2 && addr < 0x14) { addr = 0x14; } Signed-off-by: Peter Maydell Reviewed-by: Edgar E. Iglesias Reviewed-by: Richard Henderson Message-id: 20220117131953.3936137-1-peter.maydell@linaro.org --- target/arm/helper.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index 4df1239402..6dd241fbef 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -9658,7 +9658,7 @@ static void arm_cpu_do_interrupt_aarch32_hyp(CPUState *cs) * separately here. * * The vector table entry used is always the 0x14 Hyp mode entry point, - * unless this is an UNDEF/HVC/abort taken from Hyp to Hyp. + * unless this is an UNDEF/SVC/HVC/abort taken from Hyp to Hyp. * The offset applied to the preferred return address is always zero * (see DDI0487C.a section G1.12.3). * PSTATE A/I/F masks are set based only on the SCR.EA/IRQ/FIQ values. @@ -9672,7 +9672,7 @@ static void arm_cpu_do_interrupt_aarch32_hyp(CPUState *cs) addr = 0x04; break; case EXCP_SWI: - addr = 0x14; + addr = 0x08; break; case EXCP_BKPT: /* Fall through to prefetch abort. */ From 04e114049406dbb69fc9043c795ddd28fdba31a6 Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Wed, 15 Dec 2021 14:20:48 +0100 Subject: [PATCH 104/460] migration: All this fields are unsigned MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit So printing it as %d is wrong. Notice that for the channel id, that is an uint8_t, but I changed it anyways for consistency. Signed-off-by: Juan Quintela Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Peter Xu --- migration/multifd-zlib.c | 20 ++++++++++---------- migration/multifd-zstd.c | 24 ++++++++++++------------ migration/multifd.c | 16 ++++++++-------- migration/trace-events | 26 +++++++++++++------------- 4 files changed, 43 insertions(+), 43 deletions(-) diff --git a/migration/multifd-zlib.c b/migration/multifd-zlib.c index da6201704c..9f6ebf1076 100644 --- a/migration/multifd-zlib.c +++ b/migration/multifd-zlib.c @@ -51,7 +51,7 @@ static int zlib_send_setup(MultiFDSendParams *p, Error **errp) zs->opaque = Z_NULL; if (deflateInit(zs, migrate_multifd_zlib_level()) != Z_OK) { g_free(z); - error_setg(errp, "multifd %d: deflate init failed", p->id); + error_setg(errp, "multifd %u: deflate init failed", p->id); return -1; } /* To be safe, we reserve twice the size of the packet */ @@ -60,7 +60,7 @@ static int zlib_send_setup(MultiFDSendParams *p, Error **errp) if (!z->zbuff) { deflateEnd(&z->zs); g_free(z); - error_setg(errp, "multifd %d: out of memory for zbuff", p->id); + error_setg(errp, "multifd %u: out of memory for zbuff", p->id); return -1; } p->data = z; @@ -132,12 +132,12 @@ static int zlib_send_prepare(MultiFDSendParams *p, Error **errp) ret = deflate(zs, flush); } while (ret == Z_OK && zs->avail_in && zs->avail_out); if (ret == Z_OK && zs->avail_in) { - error_setg(errp, "multifd %d: deflate failed to compress all input", + error_setg(errp, "multifd %u: deflate failed to compress all input", p->id); return -1; } if (ret != Z_OK) { - error_setg(errp, "multifd %d: deflate returned %d instead of Z_OK", + error_setg(errp, "multifd %u: deflate returned %d instead of Z_OK", p->id, ret); return -1; } @@ -190,7 +190,7 @@ static int zlib_recv_setup(MultiFDRecvParams *p, Error **errp) zs->avail_in = 0; zs->next_in = Z_NULL; if (inflateInit(zs) != Z_OK) { - error_setg(errp, "multifd %d: inflate init failed", p->id); + error_setg(errp, "multifd %u: inflate init failed", p->id); return -1; } /* To be safe, we reserve twice the size of the packet */ @@ -198,7 +198,7 @@ static int zlib_recv_setup(MultiFDRecvParams *p, Error **errp) z->zbuff = g_try_malloc(z->zbuff_len); if (!z->zbuff) { inflateEnd(zs); - error_setg(errp, "multifd %d: out of memory for zbuff", p->id); + error_setg(errp, "multifd %u: out of memory for zbuff", p->id); return -1; } return 0; @@ -247,7 +247,7 @@ static int zlib_recv_pages(MultiFDRecvParams *p, Error **errp) int i; if (flags != MULTIFD_FLAG_ZLIB) { - error_setg(errp, "multifd %d: flags received %x flags expected %x", + error_setg(errp, "multifd %u: flags received %x flags expected %x", p->id, flags, MULTIFD_FLAG_ZLIB); return -1; } @@ -284,19 +284,19 @@ static int zlib_recv_pages(MultiFDRecvParams *p, Error **errp) } while (ret == Z_OK && zs->avail_in && (zs->total_out - start) < page_size); if (ret == Z_OK && (zs->total_out - start) < page_size) { - error_setg(errp, "multifd %d: inflate generated too few output", + error_setg(errp, "multifd %u: inflate generated too few output", p->id); return -1; } if (ret != Z_OK) { - error_setg(errp, "multifd %d: inflate returned %d instead of Z_OK", + error_setg(errp, "multifd %u: inflate returned %d instead of Z_OK", p->id, ret); return -1; } } out_size = zs->total_out - out_size; if (out_size != expected_size) { - error_setg(errp, "multifd %d: packet size received %d size expected %d", + error_setg(errp, "multifd %u: packet size received %u size expected %u", p->id, out_size, expected_size); return -1; } diff --git a/migration/multifd-zstd.c b/migration/multifd-zstd.c index 2d5b61106c..cc4e991724 100644 --- a/migration/multifd-zstd.c +++ b/migration/multifd-zstd.c @@ -55,7 +55,7 @@ static int zstd_send_setup(MultiFDSendParams *p, Error **errp) z->zcs = ZSTD_createCStream(); if (!z->zcs) { g_free(z); - error_setg(errp, "multifd %d: zstd createCStream failed", p->id); + error_setg(errp, "multifd %u: zstd createCStream failed", p->id); return -1; } @@ -63,7 +63,7 @@ static int zstd_send_setup(MultiFDSendParams *p, Error **errp) if (ZSTD_isError(res)) { ZSTD_freeCStream(z->zcs); g_free(z); - error_setg(errp, "multifd %d: initCStream failed with error %s", + error_setg(errp, "multifd %u: initCStream failed with error %s", p->id, ZSTD_getErrorName(res)); return -1; } @@ -73,7 +73,7 @@ static int zstd_send_setup(MultiFDSendParams *p, Error **errp) if (!z->zbuff) { ZSTD_freeCStream(z->zcs); g_free(z); - error_setg(errp, "multifd %d: out of memory for zbuff", p->id); + error_setg(errp, "multifd %u: out of memory for zbuff", p->id); return -1; } return 0; @@ -144,12 +144,12 @@ static int zstd_send_prepare(MultiFDSendParams *p, Error **errp) } while (ret > 0 && (z->in.size - z->in.pos > 0) && (z->out.size - z->out.pos > 0)); if (ret > 0 && (z->in.size - z->in.pos > 0)) { - error_setg(errp, "multifd %d: compressStream buffer too small", + error_setg(errp, "multifd %u: compressStream buffer too small", p->id); return -1; } if (ZSTD_isError(ret)) { - error_setg(errp, "multifd %d: compressStream error %s", + error_setg(errp, "multifd %u: compressStream error %s", p->id, ZSTD_getErrorName(ret)); return -1; } @@ -198,7 +198,7 @@ static int zstd_recv_setup(MultiFDRecvParams *p, Error **errp) z->zds = ZSTD_createDStream(); if (!z->zds) { g_free(z); - error_setg(errp, "multifd %d: zstd createDStream failed", p->id); + error_setg(errp, "multifd %u: zstd createDStream failed", p->id); return -1; } @@ -206,7 +206,7 @@ static int zstd_recv_setup(MultiFDRecvParams *p, Error **errp) if (ZSTD_isError(ret)) { ZSTD_freeDStream(z->zds); g_free(z); - error_setg(errp, "multifd %d: initDStream failed with error %s", + error_setg(errp, "multifd %u: initDStream failed with error %s", p->id, ZSTD_getErrorName(ret)); return -1; } @@ -217,7 +217,7 @@ static int zstd_recv_setup(MultiFDRecvParams *p, Error **errp) if (!z->zbuff) { ZSTD_freeDStream(z->zds); g_free(z); - error_setg(errp, "multifd %d: out of memory for zbuff", p->id); + error_setg(errp, "multifd %u: out of memory for zbuff", p->id); return -1; } return 0; @@ -265,7 +265,7 @@ static int zstd_recv_pages(MultiFDRecvParams *p, Error **errp) int i; if (flags != MULTIFD_FLAG_ZSTD) { - error_setg(errp, "multifd %d: flags received %x flags expected %x", + error_setg(errp, "multifd %u: flags received %x flags expected %x", p->id, flags, MULTIFD_FLAG_ZSTD); return -1; } @@ -297,19 +297,19 @@ static int zstd_recv_pages(MultiFDRecvParams *p, Error **errp) } while (ret > 0 && (z->in.size - z->in.pos > 0) && (z->out.pos < page_size)); if (ret > 0 && (z->out.pos < page_size)) { - error_setg(errp, "multifd %d: decompressStream buffer too small", + error_setg(errp, "multifd %u: decompressStream buffer too small", p->id); return -1; } if (ZSTD_isError(ret)) { - error_setg(errp, "multifd %d: decompressStream returned %s", + error_setg(errp, "multifd %u: decompressStream returned %s", p->id, ZSTD_getErrorName(ret)); return ret; } out_size += z->out.pos; } if (out_size != expected_size) { - error_setg(errp, "multifd %d: packet size received %d size expected %d", + error_setg(errp, "multifd %u: packet size received %u size expected %u", p->id, out_size, expected_size); return -1; } diff --git a/migration/multifd.c b/migration/multifd.c index 3242f688e5..4d62850258 100644 --- a/migration/multifd.c +++ b/migration/multifd.c @@ -148,7 +148,7 @@ static int nocomp_recv_pages(MultiFDRecvParams *p, Error **errp) uint32_t flags = p->flags & MULTIFD_FLAG_COMPRESSION_MASK; if (flags != MULTIFD_FLAG_NOCOMP) { - error_setg(errp, "multifd %d: flags received %x flags expected %x", + error_setg(errp, "multifd %u: flags received %x flags expected %x", p->id, flags, MULTIFD_FLAG_NOCOMP); return -1; } @@ -212,8 +212,8 @@ static int multifd_recv_initial_packet(QIOChannel *c, Error **errp) } if (msg.version != MULTIFD_VERSION) { - error_setg(errp, "multifd: received packet version %d " - "expected %d", msg.version, MULTIFD_VERSION); + error_setg(errp, "multifd: received packet version %u " + "expected %u", msg.version, MULTIFD_VERSION); return -1; } @@ -229,8 +229,8 @@ static int multifd_recv_initial_packet(QIOChannel *c, Error **errp) } if (msg.id > migrate_multifd_channels()) { - error_setg(errp, "multifd: received channel version %d " - "expected %d", msg.version, MULTIFD_VERSION); + error_setg(errp, "multifd: received channel version %u " + "expected %u", msg.version, MULTIFD_VERSION); return -1; } @@ -303,7 +303,7 @@ static int multifd_recv_unfill_packet(MultiFDRecvParams *p, Error **errp) packet->version = be32_to_cpu(packet->version); if (packet->version != MULTIFD_VERSION) { error_setg(errp, "multifd: received packet " - "version %d and expected version %d", + "version %u and expected version %u", packet->version, MULTIFD_VERSION); return -1; } @@ -317,7 +317,7 @@ static int multifd_recv_unfill_packet(MultiFDRecvParams *p, Error **errp) */ if (packet->pages_alloc > pages_max * 100) { error_setg(errp, "multifd: received packet " - "with size %d and expected a maximum size of %d", + "with size %u and expected a maximum size of %u", packet->pages_alloc, pages_max * 100) ; return -1; } @@ -333,7 +333,7 @@ static int multifd_recv_unfill_packet(MultiFDRecvParams *p, Error **errp) p->pages->num = be32_to_cpu(packet->pages_used); if (p->pages->num > packet->pages_alloc) { error_setg(errp, "multifd: received packet " - "with %d pages and expected maximum pages are %d", + "with %u pages and expected maximum pages are %u", p->pages->num, packet->pages_alloc) ; return -1; } diff --git a/migration/trace-events b/migration/trace-events index b48d873b8a..5172cb3b3d 100644 --- a/migration/trace-events +++ b/migration/trace-events @@ -115,23 +115,23 @@ ram_write_tracking_ramblock_start(const char *block_id, size_t page_size, void * ram_write_tracking_ramblock_stop(const char *block_id, size_t page_size, void *addr, size_t length) "%s: page_size: %zu addr: %p length: %zu" # multifd.c -multifd_new_send_channel_async(uint8_t id) "channel %d" -multifd_recv(uint8_t id, uint64_t packet_num, uint32_t used, uint32_t flags, uint32_t next_packet_size) "channel %d packet_num %" PRIu64 " pages %d flags 0x%x next packet size %d" -multifd_recv_new_channel(uint8_t id) "channel %d" +multifd_new_send_channel_async(uint8_t id) "channel %u" +multifd_recv(uint8_t id, uint64_t packet_num, uint32_t used, uint32_t flags, uint32_t next_packet_size) "channel %u packet_num %" PRIu64 " pages %u flags 0x%x next packet size %u" +multifd_recv_new_channel(uint8_t id) "channel %u" multifd_recv_sync_main(long packet_num) "packet num %ld" -multifd_recv_sync_main_signal(uint8_t id) "channel %d" -multifd_recv_sync_main_wait(uint8_t id) "channel %d" +multifd_recv_sync_main_signal(uint8_t id) "channel %u" +multifd_recv_sync_main_wait(uint8_t id) "channel %u" multifd_recv_terminate_threads(bool error) "error %d" -multifd_recv_thread_end(uint8_t id, uint64_t packets, uint64_t pages) "channel %d packets %" PRIu64 " pages %" PRIu64 -multifd_recv_thread_start(uint8_t id) "%d" -multifd_send(uint8_t id, uint64_t packet_num, uint32_t used, uint32_t flags, uint32_t next_packet_size) "channel %d packet_num %" PRIu64 " pages %d flags 0x%x next packet size %d" -multifd_send_error(uint8_t id) "channel %d" +multifd_recv_thread_end(uint8_t id, uint64_t packets, uint64_t pages) "channel %u packets %" PRIu64 " pages %" PRIu64 +multifd_recv_thread_start(uint8_t id) "%u" +multifd_send(uint8_t id, uint64_t packet_num, uint32_t used, uint32_t flags, uint32_t next_packet_size) "channel %u packet_num %" PRIu64 " pages %u flags 0x%x next packet size %u" +multifd_send_error(uint8_t id) "channel %u" multifd_send_sync_main(long packet_num) "packet num %ld" -multifd_send_sync_main_signal(uint8_t id) "channel %d" -multifd_send_sync_main_wait(uint8_t id) "channel %d" +multifd_send_sync_main_signal(uint8_t id) "channel %u" +multifd_send_sync_main_wait(uint8_t id) "channel %u" multifd_send_terminate_threads(bool error) "error %d" -multifd_send_thread_end(uint8_t id, uint64_t packets, uint64_t pages) "channel %d packets %" PRIu64 " pages %" PRIu64 -multifd_send_thread_start(uint8_t id) "%d" +multifd_send_thread_end(uint8_t id, uint64_t packets, uint64_t pages) "channel %u packets %" PRIu64 " pages %" PRIu64 +multifd_send_thread_start(uint8_t id) "%u" multifd_tls_outgoing_handshake_start(void *ioc, void *tioc, const char *hostname) "ioc=%p tioc=%p hostname=%s" multifd_tls_outgoing_handshake_error(void *ioc, const char *err) "ioc=%p err=%s" multifd_tls_outgoing_handshake_complete(void *ioc) "ioc=%p" From 05931ec561a83a6054d0658ed2b9570f5175728a Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Wed, 15 Dec 2021 19:01:21 +0100 Subject: [PATCH 105/460] migration: We only need last_stage in two places We only need last_stage in two places and we are passing it all around. Just add a field to RAMState that passes it. Signed-off-by: Juan Quintela Reviewed-by: Peter Xu --- Repeat subject (philmd suggestion) --- migration/ram.c | 41 ++++++++++++++++++----------------------- 1 file changed, 18 insertions(+), 23 deletions(-) diff --git a/migration/ram.c b/migration/ram.c index 57efa67f20..7223b0d8ca 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -325,7 +325,8 @@ struct RAMState { uint64_t xbzrle_bytes_prev; /* Start using XBZRLE (e.g., after the first round). */ bool xbzrle_enabled; - + /* Are we on the last stage of migration */ + bool last_stage; /* compression statistics since the beginning of the period */ /* amount of count that no free thread to compress data */ uint64_t compress_thread_busy_prev; @@ -683,11 +684,10 @@ static void xbzrle_cache_zero_page(RAMState *rs, ram_addr_t current_addr) * @current_addr: addr of the page * @block: block that contains the page we want to send * @offset: offset inside the block for the page - * @last_stage: if we are at the completion stage */ static int save_xbzrle_page(RAMState *rs, uint8_t **current_data, ram_addr_t current_addr, RAMBlock *block, - ram_addr_t offset, bool last_stage) + ram_addr_t offset) { int encoded_len = 0, bytes_xbzrle; uint8_t *prev_cached_page; @@ -695,7 +695,7 @@ static int save_xbzrle_page(RAMState *rs, uint8_t **current_data, if (!cache_is_cached(XBZRLE.cache, current_addr, ram_counters.dirty_sync_count)) { xbzrle_counters.cache_miss++; - if (!last_stage) { + if (!rs->last_stage) { if (cache_insert(XBZRLE.cache, current_addr, *current_data, ram_counters.dirty_sync_count) == -1) { return -1; @@ -734,7 +734,7 @@ static int save_xbzrle_page(RAMState *rs, uint8_t **current_data, * Update the cache contents, so that it corresponds to the data * sent, in all cases except where we skip the page. */ - if (!last_stage && encoded_len != 0) { + if (!rs->last_stage && encoded_len != 0) { memcpy(prev_cached_page, XBZRLE.current_buf, TARGET_PAGE_SIZE); /* * In the case where we couldn't compress, ensure that the caller @@ -1290,9 +1290,8 @@ static int save_normal_page(RAMState *rs, RAMBlock *block, ram_addr_t offset, * @rs: current RAM state * @block: block that contains the page we want to send * @offset: offset inside the block for the page - * @last_stage: if we are at the completion stage */ -static int ram_save_page(RAMState *rs, PageSearchStatus *pss, bool last_stage) +static int ram_save_page(RAMState *rs, PageSearchStatus *pss) { int pages = -1; uint8_t *p; @@ -1307,8 +1306,8 @@ static int ram_save_page(RAMState *rs, PageSearchStatus *pss, bool last_stage) XBZRLE_cache_lock(); if (rs->xbzrle_enabled && !migration_in_postcopy()) { pages = save_xbzrle_page(rs, &p, current_addr, block, - offset, last_stage); - if (!last_stage) { + offset); + if (!rs->last_stage) { /* Can't send this cached data async, since the cache page * might get updated before it gets to the wire */ @@ -2129,10 +2128,8 @@ static bool save_compress_page(RAMState *rs, RAMBlock *block, ram_addr_t offset) * * @rs: current RAM state * @pss: data about the page we want to send - * @last_stage: if we are at the completion stage */ -static int ram_save_target_page(RAMState *rs, PageSearchStatus *pss, - bool last_stage) +static int ram_save_target_page(RAMState *rs, PageSearchStatus *pss) { RAMBlock *block = pss->block; ram_addr_t offset = ((ram_addr_t)pss->page) << TARGET_PAGE_BITS; @@ -2171,7 +2168,7 @@ static int ram_save_target_page(RAMState *rs, PageSearchStatus *pss, return ram_save_multifd_page(rs, block, offset); } - return ram_save_page(rs, pss, last_stage); + return ram_save_page(rs, pss); } /** @@ -2190,10 +2187,8 @@ static int ram_save_target_page(RAMState *rs, PageSearchStatus *pss, * @rs: current RAM state * @ms: current migration state * @pss: data about the page we want to send - * @last_stage: if we are at the completion stage */ -static int ram_save_host_page(RAMState *rs, PageSearchStatus *pss, - bool last_stage) +static int ram_save_host_page(RAMState *rs, PageSearchStatus *pss) { int tmppages, pages = 0; size_t pagesize_bits = @@ -2211,7 +2206,7 @@ static int ram_save_host_page(RAMState *rs, PageSearchStatus *pss, do { /* Check the pages is dirty and if it is send it */ if (migration_bitmap_clear_dirty(rs, pss->block, pss->page)) { - tmppages = ram_save_target_page(rs, pss, last_stage); + tmppages = ram_save_target_page(rs, pss); if (tmppages < 0) { return tmppages; } @@ -2245,13 +2240,11 @@ static int ram_save_host_page(RAMState *rs, PageSearchStatus *pss, * or negative on error * * @rs: current RAM state - * @last_stage: if we are at the completion stage * * On systems where host-page-size > target-page-size it will send all the * pages in a host page that are dirty. */ - -static int ram_find_and_save_block(RAMState *rs, bool last_stage) +static int ram_find_and_save_block(RAMState *rs) { PageSearchStatus pss; int pages = 0; @@ -2280,7 +2273,7 @@ static int ram_find_and_save_block(RAMState *rs, bool last_stage) } if (found) { - pages = ram_save_host_page(rs, &pss, last_stage); + pages = ram_save_host_page(rs, &pss); } } while (!pages && again); @@ -3080,7 +3073,7 @@ static int ram_save_iterate(QEMUFile *f, void *opaque) break; } - pages = ram_find_and_save_block(rs, false); + pages = ram_find_and_save_block(rs); /* no more pages to sent */ if (pages == 0) { done = 1; @@ -3160,6 +3153,8 @@ static int ram_save_complete(QEMUFile *f, void *opaque) RAMState *rs = *temp; int ret = 0; + rs->last_stage = !migration_in_colo_state(); + WITH_RCU_READ_LOCK_GUARD() { if (!migration_in_postcopy()) { migration_bitmap_sync_precopy(rs); @@ -3173,7 +3168,7 @@ static int ram_save_complete(QEMUFile *f, void *opaque) while (true) { int pages; - pages = ram_find_and_save_block(rs, !migration_in_colo_state()); + pages = ram_find_and_save_block(rs); /* no more blocks to sent */ if (pages == 0) { break; From 0189c722917ce3626e21bcad675871228626d61b Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Wed, 15 Dec 2021 20:34:47 +0100 Subject: [PATCH 106/460] migration: ram_release_pages() always receive 1 page as argument MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove the pages argument. And s/pages/page/ Signed-off-by: Juan Quintela Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Peter Xu --- - Use 1LL instead of casts (philmd) - Change the whole 1ULL for TARGET_PAGE_SIZE --- migration/ram.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/migration/ram.c b/migration/ram.c index 7223b0d8ca..881fe4974e 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -1204,13 +1204,13 @@ static int save_zero_page(RAMState *rs, RAMBlock *block, ram_addr_t offset) return -1; } -static void ram_release_pages(const char *rbname, uint64_t offset, int pages) +static void ram_release_page(const char *rbname, uint64_t offset) { if (!migrate_release_ram() || !migration_in_postcopy()) { return; } - ram_discard_range(rbname, offset, ((ram_addr_t)pages) << TARGET_PAGE_BITS); + ram_discard_range(rbname, offset, TARGET_PAGE_SIZE); } /* @@ -1365,7 +1365,7 @@ static bool do_compress_ram_page(QEMUFile *f, z_stream *stream, RAMBlock *block, } exit: - ram_release_pages(block->idstr, offset & TARGET_PAGE_MASK, 1); + ram_release_page(block->idstr, offset & TARGET_PAGE_MASK); return zero_page; } @@ -2153,7 +2153,7 @@ static int ram_save_target_page(RAMState *rs, PageSearchStatus *pss) xbzrle_cache_zero_page(rs, block->offset + offset); XBZRLE_cache_unlock(); } - ram_release_pages(block->idstr, offset, res); + ram_release_page(block->idstr, offset); return res; } From 20d549cb0b6c8a9df8690d8e97505aa785036472 Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Tue, 21 Dec 2021 10:28:16 +0100 Subject: [PATCH 107/460] migration: Remove masking for compression Remove the mask in the call to ram_release_pages(). Nothing else does it, and if the offset has that bits set, we have a lot of trouble. Signed-off-by: Juan Quintela Reviewed-by: Dr. David Alan Gilbert --- migration/ram.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/migration/ram.c b/migration/ram.c index 881fe4974e..fa49d22e69 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -1340,7 +1340,7 @@ static bool do_compress_ram_page(QEMUFile *f, z_stream *stream, RAMBlock *block, ram_addr_t offset, uint8_t *source_buf) { RAMState *rs = ram_state; - uint8_t *p = block->host + (offset & TARGET_PAGE_MASK); + uint8_t *p = block->host + offset; bool zero_page = false; int ret; @@ -1365,7 +1365,7 @@ static bool do_compress_ram_page(QEMUFile *f, z_stream *stream, RAMBlock *block, } exit: - ram_release_page(block->idstr, offset & TARGET_PAGE_MASK); + ram_release_page(block->idstr, offset); return zero_page; } From e7f2e190e5d522589cb6db327a99960137a7473b Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Thu, 16 Dec 2021 09:39:49 +0100 Subject: [PATCH 108/460] migration: simplify do_compress_ram_page The goto is not needed at all. Signed-off-by: Juan Quintela Reviewed-by: Dr. David Alan Gilbert --- migration/ram.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/migration/ram.c b/migration/ram.c index fa49d22e69..422c6bce28 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -1341,12 +1341,11 @@ static bool do_compress_ram_page(QEMUFile *f, z_stream *stream, RAMBlock *block, { RAMState *rs = ram_state; uint8_t *p = block->host + offset; - bool zero_page = false; int ret; if (save_zero_page_to_file(rs, f, block, offset)) { - zero_page = true; - goto exit; + ram_release_page(block->idstr, offset); + return true; } save_page_header(rs, f, block, offset | RAM_SAVE_FLAG_COMPRESS_PAGE); @@ -1361,12 +1360,8 @@ static bool do_compress_ram_page(QEMUFile *f, z_stream *stream, RAMBlock *block, if (ret < 0) { qemu_file_set_error(migrate_get_current()->to_dst_file, ret); error_report("compressed data failed!"); - return false; } - -exit: - ram_release_page(block->idstr, offset); - return zero_page; + return false; } static void From 47fe16ff666bf1d106dd7d74e94ba2dc050b1c95 Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Thu, 16 Dec 2021 09:58:49 +0100 Subject: [PATCH 109/460] migration: Move ram_release_pages() call to save_zero_page_to_file() We always need to call it when we find a zero page, so put it in a single place. Signed-off-by: Juan Quintela Reviewed-by: Peter Xu --- migration/ram.c | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/migration/ram.c b/migration/ram.c index 422c6bce28..e9dcd3ca4e 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -1158,6 +1158,15 @@ static void migration_bitmap_sync_precopy(RAMState *rs) } } +static void ram_release_page(const char *rbname, uint64_t offset) +{ + if (!migrate_release_ram() || !migration_in_postcopy()) { + return; + } + + ram_discard_range(rbname, offset, TARGET_PAGE_SIZE); +} + /** * save_zero_page_to_file: send the zero page to the file * @@ -1179,6 +1188,7 @@ static int save_zero_page_to_file(RAMState *rs, QEMUFile *file, len += save_page_header(rs, file, block, offset | RAM_SAVE_FLAG_ZERO); qemu_put_byte(file, 0); len += 1; + ram_release_page(block->idstr, offset); } return len; } @@ -1204,15 +1214,6 @@ static int save_zero_page(RAMState *rs, RAMBlock *block, ram_addr_t offset) return -1; } -static void ram_release_page(const char *rbname, uint64_t offset) -{ - if (!migrate_release_ram() || !migration_in_postcopy()) { - return; - } - - ram_discard_range(rbname, offset, TARGET_PAGE_SIZE); -} - /* * @pages: the number of pages written by the control path, * < 0 - error @@ -1344,7 +1345,6 @@ static bool do_compress_ram_page(QEMUFile *f, z_stream *stream, RAMBlock *block, int ret; if (save_zero_page_to_file(rs, f, block, offset)) { - ram_release_page(block->idstr, offset); return true; } @@ -2148,7 +2148,6 @@ static int ram_save_target_page(RAMState *rs, PageSearchStatus *pss) xbzrle_cache_zero_page(rs, block->offset + offset); XBZRLE_cache_unlock(); } - ram_release_page(block->idstr, offset); return res; } From fc6705229cbca807f2bab9a8acf22b0ab249e55c Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Fri, 26 Nov 2021 10:30:32 +0100 Subject: [PATCH 110/460] multifd: Use proper maximum compression values It happens that there are functions to calculate the worst possible compression size for a packet. Use them. Suggested-by: Dr. David Alan Gilbert Signed-off-by: Juan Quintela Reviewed-by: Dr. David Alan Gilbert --- migration/multifd-zlib.c | 4 ++-- migration/multifd-zstd.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/migration/multifd-zlib.c b/migration/multifd-zlib.c index 9f6ebf1076..a2fec4d01d 100644 --- a/migration/multifd-zlib.c +++ b/migration/multifd-zlib.c @@ -54,8 +54,8 @@ static int zlib_send_setup(MultiFDSendParams *p, Error **errp) error_setg(errp, "multifd %u: deflate init failed", p->id); return -1; } - /* To be safe, we reserve twice the size of the packet */ - z->zbuff_len = MULTIFD_PACKET_SIZE * 2; + /* This is the maxium size of the compressed buffer */ + z->zbuff_len = compressBound(MULTIFD_PACKET_SIZE); z->zbuff = g_try_malloc(z->zbuff_len); if (!z->zbuff) { deflateEnd(&z->zs); diff --git a/migration/multifd-zstd.c b/migration/multifd-zstd.c index cc4e991724..97c08367d0 100644 --- a/migration/multifd-zstd.c +++ b/migration/multifd-zstd.c @@ -67,8 +67,8 @@ static int zstd_send_setup(MultiFDSendParams *p, Error **errp) p->id, ZSTD_getErrorName(res)); return -1; } - /* To be safe, we reserve twice the size of the packet */ - z->zbuff_len = MULTIFD_PACKET_SIZE * 2; + /* This is the maxium size of the compressed buffer */ + z->zbuff_len = ZSTD_compressBound(MULTIFD_PACKET_SIZE); z->zbuff = g_try_malloc(z->zbuff_len); if (!z->zbuff) { ZSTD_freeCStream(z->zcs); From 226468ba3dea950ab4bb0b729878dde25812da1c Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Fri, 19 Nov 2021 12:06:05 +0100 Subject: [PATCH 111/460] multifd: Move iov from pages to params This will allow us to reduce the number of system calls on the next patch. Signed-off-by: Juan Quintela --- migration/multifd.c | 34 ++++++++++++++++++++++++---------- migration/multifd.h | 8 ++++++-- 2 files changed, 30 insertions(+), 12 deletions(-) diff --git a/migration/multifd.c b/migration/multifd.c index 4d62850258..f75bd3c188 100644 --- a/migration/multifd.c +++ b/migration/multifd.c @@ -86,7 +86,16 @@ static void nocomp_send_cleanup(MultiFDSendParams *p, Error **errp) */ static int nocomp_send_prepare(MultiFDSendParams *p, Error **errp) { - p->next_packet_size = p->pages->num * qemu_target_page_size(); + MultiFDPages_t *pages = p->pages; + size_t page_size = qemu_target_page_size(); + + for (int i = 0; i < p->pages->num; i++) { + p->iov[p->iovs_num].iov_base = pages->block->host + pages->offset[i]; + p->iov[p->iovs_num].iov_len = page_size; + p->iovs_num++; + } + + p->next_packet_size = p->pages->num * page_size; p->flags |= MULTIFD_FLAG_NOCOMP; return 0; } @@ -104,7 +113,7 @@ static int nocomp_send_prepare(MultiFDSendParams *p, Error **errp) */ static int nocomp_send_write(MultiFDSendParams *p, uint32_t used, Error **errp) { - return qio_channel_writev_all(p->c, p->pages->iov, used, errp); + return qio_channel_writev_all(p->c, p->iov, p->iovs_num, errp); } /** @@ -146,13 +155,18 @@ static void nocomp_recv_cleanup(MultiFDRecvParams *p) static int nocomp_recv_pages(MultiFDRecvParams *p, Error **errp) { uint32_t flags = p->flags & MULTIFD_FLAG_COMPRESSION_MASK; + size_t page_size = qemu_target_page_size(); if (flags != MULTIFD_FLAG_NOCOMP) { error_setg(errp, "multifd %u: flags received %x flags expected %x", p->id, flags, MULTIFD_FLAG_NOCOMP); return -1; } - return qio_channel_readv_all(p->c, p->pages->iov, p->pages->num, errp); + for (int i = 0; i < p->pages->num; i++) { + p->iov[i].iov_base = p->pages->block->host + p->pages->offset[i]; + p->iov[i].iov_len = page_size; + } + return qio_channel_readv_all(p->c, p->iov, p->pages->num, errp); } static MultiFDMethods multifd_nocomp_ops = { @@ -242,7 +256,6 @@ static MultiFDPages_t *multifd_pages_init(size_t size) MultiFDPages_t *pages = g_new0(MultiFDPages_t, 1); pages->allocated = size; - pages->iov = g_new0(struct iovec, size); pages->offset = g_new0(ram_addr_t, size); return pages; @@ -254,8 +267,6 @@ static void multifd_pages_clear(MultiFDPages_t *pages) pages->allocated = 0; pages->packet_num = 0; pages->block = NULL; - g_free(pages->iov); - pages->iov = NULL; g_free(pages->offset); pages->offset = NULL; g_free(pages); @@ -365,8 +376,6 @@ static int multifd_recv_unfill_packet(MultiFDRecvParams *p, Error **errp) return -1; } p->pages->offset[i] = offset; - p->pages->iov[i].iov_base = block->host + offset; - p->pages->iov[i].iov_len = page_size; } return 0; @@ -470,8 +479,6 @@ int multifd_queue_page(QEMUFile *f, RAMBlock *block, ram_addr_t offset) if (pages->block == block) { pages->offset[pages->num] = offset; - pages->iov[pages->num].iov_base = block->host + offset; - pages->iov[pages->num].iov_len = qemu_target_page_size(); pages->num++; if (pages->num < pages->allocated) { @@ -567,6 +574,8 @@ void multifd_save_cleanup(void) 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, &local_err); if (local_err) { migrate_set_error(migrate_get_current(), local_err); @@ -654,6 +663,7 @@ static void *multifd_send_thread(void *opaque) uint32_t used = p->pages->num; uint64_t packet_num = p->packet_num; uint32_t flags = p->flags; + p->iovs_num = 0; if (used) { ret = multifd_send_state->ops->send_prepare(p, &local_err); @@ -922,6 +932,7 @@ int multifd_save_setup(Error **errp) p->packet->version = cpu_to_be32(MULTIFD_VERSION); p->name = g_strdup_printf("multifdsend_%d", i); p->tls_hostname = g_strdup(s->hostname); + p->iov = g_new0(struct iovec, page_count); socket_send_channel_create(multifd_new_send_channel_async, p); } @@ -1021,6 +1032,8 @@ int multifd_load_cleanup(Error **errp) p->packet_len = 0; g_free(p->packet); p->packet = NULL; + g_free(p->iov); + p->iov = NULL; multifd_recv_state->ops->recv_cleanup(p); } qemu_sem_destroy(&multifd_recv_state->sem_sync); @@ -1161,6 +1174,7 @@ int multifd_load_setup(Error **errp) + sizeof(uint64_t) * page_count; p->packet = g_malloc0(p->packet_len); p->name = g_strdup_printf("multifdrecv_%d", i); + p->iov = g_new0(struct iovec, page_count); } for (i = 0; i < thread_count; i++) { diff --git a/migration/multifd.h b/migration/multifd.h index e57adc783b..c3f18af364 100644 --- a/migration/multifd.h +++ b/migration/multifd.h @@ -62,8 +62,6 @@ typedef struct { uint64_t packet_num; /* offset of each page */ ram_addr_t *offset; - /* pointer to each page */ - struct iovec *iov; RAMBlock *block; } MultiFDPages_t; @@ -110,6 +108,10 @@ typedef struct { uint64_t num_pages; /* syncs main thread and channels */ QemuSemaphore sem_sync; + /* buffers to send */ + struct iovec *iov; + /* number of iovs used */ + uint32_t iovs_num; /* used for compression methods */ void *data; } MultiFDSendParams; @@ -149,6 +151,8 @@ typedef struct { uint64_t num_pages; /* syncs main thread and channels */ QemuSemaphore sem_sync; + /* buffers to recv */ + struct iovec *iov; /* used for de-compression methods */ void *data; } MultiFDRecvParams; From 48a4a44c1cde382c6b8e7792d01fe7d9b0a59c69 Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Fri, 19 Nov 2021 15:03:02 +0100 Subject: [PATCH 112/460] multifd: Make zlib use iov's Signed-off-by: Juan Quintela Reviewed-by: Dr. David Alan Gilbert --- migration/multifd-zlib.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/migration/multifd-zlib.c b/migration/multifd-zlib.c index a2fec4d01d..71480c82bb 100644 --- a/migration/multifd-zlib.c +++ b/migration/multifd-zlib.c @@ -143,6 +143,9 @@ static int zlib_send_prepare(MultiFDSendParams *p, Error **errp) } out_size += available - zs->avail_out; } + p->iov[p->iovs_num].iov_base = z->zbuff; + p->iov[p->iovs_num].iov_len = out_size; + p->iovs_num++; p->next_packet_size = out_size; p->flags |= MULTIFD_FLAG_ZLIB; @@ -162,10 +165,7 @@ static int zlib_send_prepare(MultiFDSendParams *p, Error **errp) */ static int zlib_send_write(MultiFDSendParams *p, uint32_t used, Error **errp) { - struct zlib_data *z = p->data; - - return qio_channel_write_all(p->c, (void *)z->zbuff, p->next_packet_size, - errp); + return qio_channel_writev_all(p->c, p->iov, p->iovs_num, errp); } /** From 0a818b89eb8eaf79ae651405907d8110a0935cfd Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Fri, 19 Nov 2021 15:05:23 +0100 Subject: [PATCH 113/460] multifd: Make zstd use iov's Signed-off-by: Juan Quintela Reviewed-by: Dr. David Alan Gilbert --- migration/multifd-zstd.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/migration/multifd-zstd.c b/migration/multifd-zstd.c index 97c08367d0..bd393aee0d 100644 --- a/migration/multifd-zstd.c +++ b/migration/multifd-zstd.c @@ -154,6 +154,9 @@ static int zstd_send_prepare(MultiFDSendParams *p, Error **errp) return -1; } } + p->iov[p->iovs_num].iov_base = z->zbuff; + p->iov[p->iovs_num].iov_len = z->out.pos; + p->iovs_num++; p->next_packet_size = z->out.pos; p->flags |= MULTIFD_FLAG_ZSTD; @@ -173,10 +176,7 @@ static int zstd_send_prepare(MultiFDSendParams *p, Error **errp) */ static int zstd_send_write(MultiFDSendParams *p, uint32_t used, Error **errp) { - struct zstd_data *z = p->data; - - return qio_channel_write_all(p->c, (void *)z->zbuff, p->next_packet_size, - errp); + return qio_channel_writev_all(p->c, p->iov, p->iovs_num, errp); } /** From 468fcb5dd0c965e1af0da9efab09b1462631da18 Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Fri, 19 Nov 2021 15:08:16 +0100 Subject: [PATCH 114/460] multifd: Remove send_write() method Everything use now iov's. Signed-off-by: Juan Quintela Reviewed-by: Dr. David Alan Gilbert --- migration/multifd-zlib.c | 17 ----------------- migration/multifd-zstd.c | 17 ----------------- migration/multifd.c | 20 ++------------------ migration/multifd.h | 2 -- 4 files changed, 2 insertions(+), 54 deletions(-) diff --git a/migration/multifd-zlib.c b/migration/multifd-zlib.c index 71480c82bb..ba90f1aaf4 100644 --- a/migration/multifd-zlib.c +++ b/migration/multifd-zlib.c @@ -152,22 +152,6 @@ static int zlib_send_prepare(MultiFDSendParams *p, Error **errp) return 0; } -/** - * zlib_send_write: do the actual write of the data - * - * Do the actual write of the comprresed buffer. - * - * Returns 0 for success or -1 for error - * - * @p: Params for the channel that we are using - * @used: number of pages used - * @errp: pointer to an error - */ -static int zlib_send_write(MultiFDSendParams *p, uint32_t used, Error **errp) -{ - return qio_channel_writev_all(p->c, p->iov, p->iovs_num, errp); -} - /** * zlib_recv_setup: setup receive side * @@ -307,7 +291,6 @@ static MultiFDMethods multifd_zlib_ops = { .send_setup = zlib_send_setup, .send_cleanup = zlib_send_cleanup, .send_prepare = zlib_send_prepare, - .send_write = zlib_send_write, .recv_setup = zlib_recv_setup, .recv_cleanup = zlib_recv_cleanup, .recv_pages = zlib_recv_pages diff --git a/migration/multifd-zstd.c b/migration/multifd-zstd.c index bd393aee0d..757434d1ee 100644 --- a/migration/multifd-zstd.c +++ b/migration/multifd-zstd.c @@ -163,22 +163,6 @@ static int zstd_send_prepare(MultiFDSendParams *p, Error **errp) return 0; } -/** - * zstd_send_write: do the actual write of the data - * - * Do the actual write of the comprresed buffer. - * - * Returns 0 for success or -1 for error - * - * @p: Params for the channel that we are using - * @used: number of pages used - * @errp: pointer to an error - */ -static int zstd_send_write(MultiFDSendParams *p, uint32_t used, Error **errp) -{ - return qio_channel_writev_all(p->c, p->iov, p->iovs_num, errp); -} - /** * zstd_recv_setup: setup receive side * @@ -320,7 +304,6 @@ static MultiFDMethods multifd_zstd_ops = { .send_setup = zstd_send_setup, .send_cleanup = zstd_send_cleanup, .send_prepare = zstd_send_prepare, - .send_write = zstd_send_write, .recv_setup = zstd_recv_setup, .recv_cleanup = zstd_recv_cleanup, .recv_pages = zstd_recv_pages diff --git a/migration/multifd.c b/migration/multifd.c index f75bd3c188..96b9cc0d8b 100644 --- a/migration/multifd.c +++ b/migration/multifd.c @@ -100,22 +100,6 @@ static int nocomp_send_prepare(MultiFDSendParams *p, Error **errp) return 0; } -/** - * nocomp_send_write: do the actual write of the data - * - * For no compression we just have to write the data. - * - * Returns 0 for success or -1 for error - * - * @p: Params for the channel that we are using - * @used: number of pages used - * @errp: pointer to an error - */ -static int nocomp_send_write(MultiFDSendParams *p, uint32_t used, Error **errp) -{ - return qio_channel_writev_all(p->c, p->iov, p->iovs_num, errp); -} - /** * nocomp_recv_setup: setup receive side * @@ -173,7 +157,6 @@ static MultiFDMethods multifd_nocomp_ops = { .send_setup = nocomp_send_setup, .send_cleanup = nocomp_send_cleanup, .send_prepare = nocomp_send_prepare, - .send_write = nocomp_send_write, .recv_setup = nocomp_recv_setup, .recv_cleanup = nocomp_recv_cleanup, .recv_pages = nocomp_recv_pages @@ -690,7 +673,8 @@ static void *multifd_send_thread(void *opaque) } if (used) { - ret = multifd_send_state->ops->send_write(p, used, &local_err); + ret = qio_channel_writev_all(p->c, p->iov, p->iovs_num, + &local_err); if (ret != 0) { break; } diff --git a/migration/multifd.h b/migration/multifd.h index c3f18af364..7496f951a7 100644 --- a/migration/multifd.h +++ b/migration/multifd.h @@ -164,8 +164,6 @@ typedef struct { void (*send_cleanup)(MultiFDSendParams *p, Error **errp); /* Prepare the send packet */ int (*send_prepare)(MultiFDSendParams *p, Error **errp); - /* Write the send packet */ - int (*send_write)(MultiFDSendParams *p, uint32_t used, Error **errp); /* Setup for receiving side */ int (*recv_setup)(MultiFDRecvParams *p, Error **errp); /* Cleanup for receiving side */ From d48c3a044537689866fe44e65d24c7d39a68868a Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Fri, 19 Nov 2021 15:35:58 +0100 Subject: [PATCH 115/460] multifd: Use a single writev on the send side Until now, we wrote the packet header with write(), and the rest of the pages with writev(). Just increase the size of the iovec and do a single writev(). Signed-off-by: Juan Quintela Reviewed-by: Dr. David Alan Gilbert --- migration/multifd.c | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/migration/multifd.c b/migration/multifd.c index 96b9cc0d8b..1d4885e1a0 100644 --- a/migration/multifd.c +++ b/migration/multifd.c @@ -646,7 +646,7 @@ static void *multifd_send_thread(void *opaque) uint32_t used = p->pages->num; uint64_t packet_num = p->packet_num; uint32_t flags = p->flags; - p->iovs_num = 0; + p->iovs_num = 1; if (used) { ret = multifd_send_state->ops->send_prepare(p, &local_err); @@ -666,20 +666,15 @@ static void *multifd_send_thread(void *opaque) trace_multifd_send(p->id, packet_num, used, flags, p->next_packet_size); - ret = qio_channel_write_all(p->c, (void *)p->packet, - p->packet_len, &local_err); + p->iov[0].iov_len = p->packet_len; + p->iov[0].iov_base = p->packet; + + ret = qio_channel_writev_all(p->c, p->iov, p->iovs_num, + &local_err); if (ret != 0) { break; } - if (used) { - ret = qio_channel_writev_all(p->c, p->iov, p->iovs_num, - &local_err); - if (ret != 0) { - break; - } - } - qemu_mutex_lock(&p->mutex); p->pending_job--; qemu_mutex_unlock(&p->mutex); @@ -916,7 +911,8 @@ int multifd_save_setup(Error **errp) p->packet->version = cpu_to_be32(MULTIFD_VERSION); p->name = g_strdup_printf("multifdsend_%d", i); p->tls_hostname = g_strdup(s->hostname); - p->iov = g_new0(struct iovec, page_count); + /* We need one extra place for the packet header */ + p->iov = g_new0(struct iovec, page_count + 1); socket_send_channel_create(multifd_new_send_channel_async, p); } From c27779a215843e92eb120de80f982f3a63d1becb Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Mon, 22 Nov 2021 13:01:29 +0100 Subject: [PATCH 116/460] multifd: Unfold "used" variable by its value Signed-off-by: Juan Quintela Reviewed-by: Dr. David Alan Gilbert --- migration/multifd.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/migration/multifd.c b/migration/multifd.c index 1d4885e1a0..e5b1fa5015 100644 --- a/migration/multifd.c +++ b/migration/multifd.c @@ -1062,7 +1062,6 @@ static void *multifd_recv_thread(void *opaque) rcu_register_thread(); while (true) { - uint32_t used; uint32_t flags; if (p->quit) { @@ -1085,17 +1084,16 @@ static void *multifd_recv_thread(void *opaque) break; } - used = p->pages->num; flags = p->flags; /* recv methods don't know how to handle the SYNC flag */ p->flags &= ~MULTIFD_FLAG_SYNC; - trace_multifd_recv(p->id, p->packet_num, used, flags, + trace_multifd_recv(p->id, p->packet_num, p->pages->num, flags, p->next_packet_size); p->num_packets++; - p->num_pages += used; + p->num_pages += p->pages->num; qemu_mutex_unlock(&p->mutex); - if (used) { + if (p->pages->num) { ret = multifd_recv_state->ops->recv_pages(p, &local_err); if (ret != 0) { break; From 815956f03902980c771da64b17f7f791c1cb57b0 Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Mon, 22 Nov 2021 13:26:18 +0100 Subject: [PATCH 117/460] multifd: Use normal pages array on the send side We are only sending normal pages through multifd channels. Later on this series, we are going to also send zero pages. We are going to detect if a page is zero or non zero in the multifd channel thread, not on the main thread. So we receive an array of pages page->offset[N] And we will end with: p->normal[N - zero_pages] p->zero[zero_pages]. In this patch, we just copy all the pages in offset to normal. for (i = 0; i < pages->num; i++) { p->narmal[p->normal_num] = pages->offset[i]; p->normal_num++: } Later in the series this becomes: for (i = 0; i < pages->num; i++) { if (buffer_is_zero(page->offset[i])) { p->zerol[p->zero_num] = pages->offset[i]; p->zero_num++: } else { p->narmal[p->normal_num] = pages->offset[i]; p->normal_num++: } } Signed-off-by: Juan Quintela Reviewed-by: Dr. David Alan Gilbert --- Improving comment (dave) Renaming num_normal_pages to total_normal_pages (peter) --- migration/multifd-zlib.c | 6 +++--- migration/multifd-zstd.c | 6 +++--- migration/multifd.c | 30 +++++++++++++++++++----------- migration/multifd.h | 8 ++++++-- migration/trace-events | 4 ++-- 5 files changed, 33 insertions(+), 21 deletions(-) diff --git a/migration/multifd-zlib.c b/migration/multifd-zlib.c index ba90f1aaf4..7f4fbef2c9 100644 --- a/migration/multifd-zlib.c +++ b/migration/multifd-zlib.c @@ -106,16 +106,16 @@ static int zlib_send_prepare(MultiFDSendParams *p, Error **errp) int ret; uint32_t i; - for (i = 0; i < p->pages->num; i++) { + for (i = 0; i < p->normal_num; i++) { uint32_t available = z->zbuff_len - out_size; int flush = Z_NO_FLUSH; - if (i == p->pages->num - 1) { + if (i == p->normal_num - 1) { flush = Z_SYNC_FLUSH; } zs->avail_in = page_size; - zs->next_in = p->pages->block->host + p->pages->offset[i]; + zs->next_in = p->pages->block->host + p->normal[i]; zs->avail_out = available; zs->next_out = z->zbuff + out_size; diff --git a/migration/multifd-zstd.c b/migration/multifd-zstd.c index 757434d1ee..907d07805c 100644 --- a/migration/multifd-zstd.c +++ b/migration/multifd-zstd.c @@ -121,13 +121,13 @@ static int zstd_send_prepare(MultiFDSendParams *p, Error **errp) z->out.size = z->zbuff_len; z->out.pos = 0; - for (i = 0; i < p->pages->num; i++) { + for (i = 0; i < p->normal_num; i++) { ZSTD_EndDirective flush = ZSTD_e_continue; - if (i == p->pages->num - 1) { + if (i == p->normal_num - 1) { flush = ZSTD_e_flush; } - z->in.src = p->pages->block->host + p->pages->offset[i]; + z->in.src = p->pages->block->host + p->normal[i]; z->in.size = page_size; z->in.pos = 0; diff --git a/migration/multifd.c b/migration/multifd.c index e5b1fa5015..7b804928a2 100644 --- a/migration/multifd.c +++ b/migration/multifd.c @@ -89,13 +89,13 @@ static int nocomp_send_prepare(MultiFDSendParams *p, Error **errp) MultiFDPages_t *pages = p->pages; size_t page_size = qemu_target_page_size(); - for (int i = 0; i < p->pages->num; i++) { - p->iov[p->iovs_num].iov_base = pages->block->host + pages->offset[i]; + for (int i = 0; i < p->normal_num; i++) { + p->iov[p->iovs_num].iov_base = pages->block->host + p->normal[i]; p->iov[p->iovs_num].iov_len = page_size; p->iovs_num++; } - p->next_packet_size = p->pages->num * page_size; + p->next_packet_size = p->normal_num * page_size; p->flags |= MULTIFD_FLAG_NOCOMP; return 0; } @@ -262,7 +262,7 @@ static void multifd_send_fill_packet(MultiFDSendParams *p) packet->flags = cpu_to_be32(p->flags); packet->pages_alloc = cpu_to_be32(p->pages->allocated); - packet->pages_used = cpu_to_be32(p->pages->num); + packet->pages_used = cpu_to_be32(p->normal_num); packet->next_packet_size = cpu_to_be32(p->next_packet_size); packet->packet_num = cpu_to_be64(p->packet_num); @@ -270,9 +270,9 @@ static void multifd_send_fill_packet(MultiFDSendParams *p) strncpy(packet->ramblock, p->pages->block->idstr, 256); } - for (i = 0; i < p->pages->num; i++) { + for (i = 0; i < p->normal_num; i++) { /* there are architectures where ram_addr_t is 32 bit */ - uint64_t temp = p->pages->offset[i]; + uint64_t temp = p->normal[i]; packet->offset[i] = cpu_to_be64(temp); } @@ -559,6 +559,8 @@ void multifd_save_cleanup(void) p->packet = NULL; g_free(p->iov); p->iov = NULL; + g_free(p->normal); + p->normal = NULL; multifd_send_state->ops->send_cleanup(p, &local_err); if (local_err) { migrate_set_error(migrate_get_current(), local_err); @@ -643,12 +645,17 @@ static void *multifd_send_thread(void *opaque) qemu_mutex_lock(&p->mutex); if (p->pending_job) { - uint32_t used = p->pages->num; uint64_t packet_num = p->packet_num; uint32_t flags = p->flags; p->iovs_num = 1; + p->normal_num = 0; - if (used) { + for (int i = 0; i < p->pages->num; i++) { + p->normal[p->normal_num] = p->pages->offset[i]; + p->normal_num++; + } + + if (p->normal_num) { ret = multifd_send_state->ops->send_prepare(p, &local_err); if (ret != 0) { qemu_mutex_unlock(&p->mutex); @@ -658,12 +665,12 @@ static void *multifd_send_thread(void *opaque) multifd_send_fill_packet(p); p->flags = 0; p->num_packets++; - p->num_pages += used; + p->total_normal_pages += p->normal_num; p->pages->num = 0; p->pages->block = NULL; qemu_mutex_unlock(&p->mutex); - trace_multifd_send(p->id, packet_num, used, flags, + trace_multifd_send(p->id, packet_num, p->normal_num, flags, p->next_packet_size); p->iov[0].iov_len = p->packet_len; @@ -713,7 +720,7 @@ out: qemu_mutex_unlock(&p->mutex); rcu_unregister_thread(); - trace_multifd_send_thread_end(p->id, p->num_packets, p->num_pages); + trace_multifd_send_thread_end(p->id, p->num_packets, p->total_normal_pages); return NULL; } @@ -913,6 +920,7 @@ int multifd_save_setup(Error **errp) p->tls_hostname = g_strdup(s->hostname); /* We need one extra place for the packet header */ p->iov = g_new0(struct iovec, page_count + 1); + p->normal = g_new0(ram_addr_t, page_count); socket_send_channel_create(multifd_new_send_channel_async, p); } diff --git a/migration/multifd.h b/migration/multifd.h index 7496f951a7..7823199dbe 100644 --- a/migration/multifd.h +++ b/migration/multifd.h @@ -104,14 +104,18 @@ typedef struct { /* thread local variables */ /* packets sent through this channel */ uint64_t num_packets; - /* pages sent through this channel */ - uint64_t num_pages; + /* non zero pages sent through this channel */ + uint64_t total_normal_pages; /* syncs main thread and channels */ QemuSemaphore sem_sync; /* buffers to send */ struct iovec *iov; /* number of iovs used */ uint32_t iovs_num; + /* Pages that are not zero */ + ram_addr_t *normal; + /* num of non zero pages */ + uint32_t normal_num; /* used for compression methods */ void *data; } MultiFDSendParams; diff --git a/migration/trace-events b/migration/trace-events index 5172cb3b3d..171a83a55d 100644 --- a/migration/trace-events +++ b/migration/trace-events @@ -124,13 +124,13 @@ multifd_recv_sync_main_wait(uint8_t id) "channel %u" multifd_recv_terminate_threads(bool error) "error %d" multifd_recv_thread_end(uint8_t id, uint64_t packets, uint64_t pages) "channel %u packets %" PRIu64 " pages %" PRIu64 multifd_recv_thread_start(uint8_t id) "%u" -multifd_send(uint8_t id, uint64_t packet_num, uint32_t used, uint32_t flags, uint32_t next_packet_size) "channel %u packet_num %" PRIu64 " pages %u flags 0x%x next packet size %u" +multifd_send(uint8_t id, uint64_t packet_num, uint32_t normal, uint32_t flags, uint32_t next_packet_size) "channel %u packet_num %" PRIu64 " normal pages %u flags 0x%x next packet size %u" multifd_send_error(uint8_t id) "channel %u" multifd_send_sync_main(long packet_num) "packet num %ld" multifd_send_sync_main_signal(uint8_t id) "channel %u" multifd_send_sync_main_wait(uint8_t id) "channel %u" multifd_send_terminate_threads(bool error) "error %d" -multifd_send_thread_end(uint8_t id, uint64_t packets, uint64_t pages) "channel %u packets %" PRIu64 " pages %" PRIu64 +multifd_send_thread_end(uint8_t id, uint64_t packets, uint64_t normal_pages) "channel %u packets %" PRIu64 " normal pages %" PRIu64 multifd_send_thread_start(uint8_t id) "%u" multifd_tls_outgoing_handshake_start(void *ioc, void *tioc, const char *hostname) "ioc=%p tioc=%p hostname=%s" multifd_tls_outgoing_handshake_error(void *ioc, const char *err) "ioc=%p err=%s" From cf2d4aa8a276f8540eef593141b7933487fa32b2 Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Mon, 22 Nov 2021 13:41:06 +0100 Subject: [PATCH 118/460] multifd: Use normal pages array on the recv side Signed-off-by: Juan Quintela Reviewed-by: Dr. David Alan Gilbert --- Rename num_normal_pages to total_normal_pages (peter) --- migration/multifd-zlib.c | 8 +++---- migration/multifd-zstd.c | 6 +++--- migration/multifd.c | 45 ++++++++++++++++++---------------------- migration/multifd.h | 8 +++++-- 4 files changed, 33 insertions(+), 34 deletions(-) diff --git a/migration/multifd-zlib.c b/migration/multifd-zlib.c index 7f4fbef2c9..8239c840d3 100644 --- a/migration/multifd-zlib.c +++ b/migration/multifd-zlib.c @@ -225,7 +225,7 @@ static int zlib_recv_pages(MultiFDRecvParams *p, Error **errp) uint32_t in_size = p->next_packet_size; /* we measure the change of total_out */ uint32_t out_size = zs->total_out; - uint32_t expected_size = p->pages->num * qemu_target_page_size(); + uint32_t expected_size = p->normal_num * page_size; uint32_t flags = p->flags & MULTIFD_FLAG_COMPRESSION_MASK; int ret; int i; @@ -244,16 +244,16 @@ static int zlib_recv_pages(MultiFDRecvParams *p, Error **errp) zs->avail_in = in_size; zs->next_in = z->zbuff; - for (i = 0; i < p->pages->num; i++) { + for (i = 0; i < p->normal_num; i++) { int flush = Z_NO_FLUSH; unsigned long start = zs->total_out; - if (i == p->pages->num - 1) { + if (i == p->normal_num - 1) { flush = Z_SYNC_FLUSH; } zs->avail_out = page_size; - zs->next_out = p->pages->block->host + p->pages->offset[i]; + zs->next_out = p->pages->block->host + p->normal[i]; /* * Welcome to inflate semantics diff --git a/migration/multifd-zstd.c b/migration/multifd-zstd.c index 907d07805c..c5ed72ddcd 100644 --- a/migration/multifd-zstd.c +++ b/migration/multifd-zstd.c @@ -242,7 +242,7 @@ static int zstd_recv_pages(MultiFDRecvParams *p, Error **errp) uint32_t in_size = p->next_packet_size; uint32_t out_size = 0; size_t page_size = qemu_target_page_size(); - uint32_t expected_size = p->pages->num * page_size; + uint32_t expected_size = p->normal_num * page_size; uint32_t flags = p->flags & MULTIFD_FLAG_COMPRESSION_MASK; struct zstd_data *z = p->data; int ret; @@ -263,8 +263,8 @@ static int zstd_recv_pages(MultiFDRecvParams *p, Error **errp) z->in.size = in_size; z->in.pos = 0; - for (i = 0; i < p->pages->num; i++) { - z->out.dst = p->pages->block->host + p->pages->offset[i]; + for (i = 0; i < p->normal_num; i++) { + z->out.dst = p->pages->block->host + p->normal[i]; z->out.size = page_size; z->out.pos = 0; diff --git a/migration/multifd.c b/migration/multifd.c index 7b804928a2..e362b1bb89 100644 --- a/migration/multifd.c +++ b/migration/multifd.c @@ -146,11 +146,11 @@ static int nocomp_recv_pages(MultiFDRecvParams *p, Error **errp) p->id, flags, MULTIFD_FLAG_NOCOMP); return -1; } - for (int i = 0; i < p->pages->num; i++) { - p->iov[i].iov_base = p->pages->block->host + p->pages->offset[i]; + for (int i = 0; i < p->normal_num; i++) { + p->iov[i].iov_base = p->pages->block->host + p->normal[i]; p->iov[i].iov_len = page_size; } - return qio_channel_readv_all(p->c, p->iov, p->pages->num, errp); + return qio_channel_readv_all(p->c, p->iov, p->normal_num, errp); } static MultiFDMethods multifd_nocomp_ops = { @@ -282,7 +282,7 @@ static int multifd_recv_unfill_packet(MultiFDRecvParams *p, Error **errp) { MultiFDPacket_t *packet = p->packet; size_t page_size = qemu_target_page_size(); - uint32_t pages_max = MULTIFD_PACKET_SIZE / page_size; + uint32_t page_count = MULTIFD_PACKET_SIZE / page_size; RAMBlock *block; int i; @@ -309,33 +309,25 @@ static int multifd_recv_unfill_packet(MultiFDRecvParams *p, Error **errp) * If we received a packet that is 100 times bigger than expected * just stop migration. It is a magic number. */ - if (packet->pages_alloc > pages_max * 100) { + if (packet->pages_alloc > page_count) { error_setg(errp, "multifd: received packet " - "with size %u and expected a maximum size of %u", - packet->pages_alloc, pages_max * 100) ; + "with size %u and expected a size of %u", + packet->pages_alloc, page_count) ; return -1; } - /* - * We received a packet that is bigger than expected but inside - * reasonable limits (see previous comment). Just reallocate. - */ - if (packet->pages_alloc > p->pages->allocated) { - multifd_pages_clear(p->pages); - p->pages = multifd_pages_init(packet->pages_alloc); - } - p->pages->num = be32_to_cpu(packet->pages_used); - if (p->pages->num > packet->pages_alloc) { + p->normal_num = be32_to_cpu(packet->pages_used); + if (p->normal_num > packet->pages_alloc) { error_setg(errp, "multifd: received packet " "with %u pages and expected maximum pages are %u", - p->pages->num, packet->pages_alloc) ; + p->normal_num, packet->pages_alloc) ; return -1; } p->next_packet_size = be32_to_cpu(packet->next_packet_size); p->packet_num = be64_to_cpu(packet->packet_num); - if (p->pages->num == 0) { + if (p->normal_num == 0) { return 0; } @@ -349,7 +341,7 @@ static int multifd_recv_unfill_packet(MultiFDRecvParams *p, Error **errp) } p->pages->block = block; - for (i = 0; i < p->pages->num; i++) { + for (i = 0; i < p->normal_num; i++) { uint64_t offset = be64_to_cpu(packet->offset[i]); if (offset > (block->used_length - page_size)) { @@ -358,7 +350,7 @@ static int multifd_recv_unfill_packet(MultiFDRecvParams *p, Error **errp) offset, block->used_length); return -1; } - p->pages->offset[i] = offset; + p->normal[i] = offset; } return 0; @@ -1022,6 +1014,8 @@ int multifd_load_cleanup(Error **errp) p->packet = NULL; g_free(p->iov); p->iov = NULL; + g_free(p->normal); + p->normal = NULL; multifd_recv_state->ops->recv_cleanup(p); } qemu_sem_destroy(&multifd_recv_state->sem_sync); @@ -1095,13 +1089,13 @@ static void *multifd_recv_thread(void *opaque) flags = p->flags; /* recv methods don't know how to handle the SYNC flag */ p->flags &= ~MULTIFD_FLAG_SYNC; - trace_multifd_recv(p->id, p->packet_num, p->pages->num, flags, + trace_multifd_recv(p->id, p->packet_num, p->normal_num, flags, p->next_packet_size); p->num_packets++; - p->num_pages += p->pages->num; + p->total_normal_pages += p->normal_num; qemu_mutex_unlock(&p->mutex); - if (p->pages->num) { + if (p->normal_num) { ret = multifd_recv_state->ops->recv_pages(p, &local_err); if (ret != 0) { break; @@ -1123,7 +1117,7 @@ static void *multifd_recv_thread(void *opaque) qemu_mutex_unlock(&p->mutex); rcu_unregister_thread(); - trace_multifd_recv_thread_end(p->id, p->num_packets, p->num_pages); + trace_multifd_recv_thread_end(p->id, p->num_packets, p->total_normal_pages); return NULL; } @@ -1161,6 +1155,7 @@ int multifd_load_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); } for (i = 0; i < thread_count; i++) { diff --git a/migration/multifd.h b/migration/multifd.h index 7823199dbe..850889c5d8 100644 --- a/migration/multifd.h +++ b/migration/multifd.h @@ -151,12 +151,16 @@ typedef struct { uint32_t next_packet_size; /* packets sent through this channel */ uint64_t num_packets; - /* pages sent through this channel */ - uint64_t num_pages; + /* non zero pages recv through this channel */ + uint64_t total_normal_pages; /* syncs main thread and channels */ QemuSemaphore sem_sync; /* buffers to recv */ struct iovec *iov; + /* Pages that are not zero */ + ram_addr_t *normal; + /* num of non zero pages */ + uint32_t normal_num; /* used for de-compression methods */ void *data; } MultiFDRecvParams; From faf60935df64f4225b89c29306e0dc3ed00e1117 Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Mon, 22 Nov 2021 14:10:57 +0100 Subject: [PATCH 119/460] multifd: recv side only needs the RAMBlock host address So we can remove the MultiFDPages. Signed-off-by: Juan Quintela Reviewed-by: Dr. David Alan Gilbert --- migration/multifd-zlib.c | 2 +- migration/multifd-zstd.c | 2 +- migration/multifd.c | 7 ++----- migration/multifd.h | 4 ++-- 4 files changed, 6 insertions(+), 9 deletions(-) diff --git a/migration/multifd-zlib.c b/migration/multifd-zlib.c index 8239c840d3..aba1c88a0c 100644 --- a/migration/multifd-zlib.c +++ b/migration/multifd-zlib.c @@ -253,7 +253,7 @@ static int zlib_recv_pages(MultiFDRecvParams *p, Error **errp) } zs->avail_out = page_size; - zs->next_out = p->pages->block->host + p->normal[i]; + zs->next_out = p->host + p->normal[i]; /* * Welcome to inflate semantics diff --git a/migration/multifd-zstd.c b/migration/multifd-zstd.c index c5ed72ddcd..d788d309f2 100644 --- a/migration/multifd-zstd.c +++ b/migration/multifd-zstd.c @@ -264,7 +264,7 @@ static int zstd_recv_pages(MultiFDRecvParams *p, Error **errp) z->in.pos = 0; for (i = 0; i < p->normal_num; i++) { - z->out.dst = p->pages->block->host + p->normal[i]; + z->out.dst = p->host + p->normal[i]; z->out.size = page_size; z->out.pos = 0; diff --git a/migration/multifd.c b/migration/multifd.c index e362b1bb89..b39fef5dfe 100644 --- a/migration/multifd.c +++ b/migration/multifd.c @@ -147,7 +147,7 @@ static int nocomp_recv_pages(MultiFDRecvParams *p, Error **errp) return -1; } for (int i = 0; i < p->normal_num; i++) { - p->iov[i].iov_base = p->pages->block->host + p->normal[i]; + p->iov[i].iov_base = p->host + p->normal[i]; p->iov[i].iov_len = page_size; } return qio_channel_readv_all(p->c, p->iov, p->normal_num, errp); @@ -340,7 +340,7 @@ static int multifd_recv_unfill_packet(MultiFDRecvParams *p, Error **errp) return -1; } - p->pages->block = block; + p->host = block->host; for (i = 0; i < p->normal_num; i++) { uint64_t offset = be64_to_cpu(packet->offset[i]); @@ -1007,8 +1007,6 @@ int multifd_load_cleanup(Error **errp) qemu_sem_destroy(&p->sem_sync); g_free(p->name); p->name = NULL; - multifd_pages_clear(p->pages); - p->pages = NULL; p->packet_len = 0; g_free(p->packet); p->packet = NULL; @@ -1149,7 +1147,6 @@ int multifd_load_setup(Error **errp) qemu_sem_init(&p->sem_sync, 0); p->quit = false; p->id = i; - p->pages = multifd_pages_init(page_count); p->packet_len = sizeof(MultiFDPacket_t) + sizeof(uint64_t) * page_count; p->packet = g_malloc0(p->packet_len); diff --git a/migration/multifd.h b/migration/multifd.h index 850889c5d8..be460f821b 100644 --- a/migration/multifd.h +++ b/migration/multifd.h @@ -136,8 +136,8 @@ typedef struct { bool running; /* should this thread finish */ bool quit; - /* array of pages to receive */ - MultiFDPages_t *pages; + /* ramblock host address */ + uint8_t *host; /* packet allocated len */ uint32_t packet_len; /* pointer to the packet */ From 8c0ec0b2b0622563a8620d0bf2bb60e90e18df18 Mon Sep 17 00:00:00 2001 From: Juan Quintela Date: Mon, 22 Nov 2021 14:13:51 +0100 Subject: [PATCH 120/460] multifd: Rename pages_used to normal_pages Signed-off-by: Juan Quintela Reviewed-by: Dr. David Alan Gilbert --- migration/multifd.c | 4 ++-- migration/multifd.h | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/migration/multifd.c b/migration/multifd.c index b39fef5dfe..76b57a7177 100644 --- a/migration/multifd.c +++ b/migration/multifd.c @@ -262,7 +262,7 @@ static void multifd_send_fill_packet(MultiFDSendParams *p) packet->flags = cpu_to_be32(p->flags); packet->pages_alloc = cpu_to_be32(p->pages->allocated); - packet->pages_used = cpu_to_be32(p->normal_num); + packet->normal_pages = cpu_to_be32(p->normal_num); packet->next_packet_size = cpu_to_be32(p->next_packet_size); packet->packet_num = cpu_to_be64(p->packet_num); @@ -316,7 +316,7 @@ static int multifd_recv_unfill_packet(MultiFDRecvParams *p, Error **errp) return -1; } - p->normal_num = be32_to_cpu(packet->pages_used); + p->normal_num = be32_to_cpu(packet->normal_pages); if (p->normal_num > packet->pages_alloc) { error_setg(errp, "multifd: received packet " "with %u pages and expected maximum pages are %u", diff --git a/migration/multifd.h b/migration/multifd.h index be460f821b..4dda900a0b 100644 --- a/migration/multifd.h +++ b/migration/multifd.h @@ -44,7 +44,8 @@ typedef struct { uint32_t flags; /* maximum number of allocated pages */ uint32_t pages_alloc; - uint32_t pages_used; + /* non zero pages */ + uint32_t normal_pages; /* size of the next packet that contains pages */ uint32_t next_packet_size; uint64_t packet_num; From 17e313406126125036191f11e9c70298be34c987 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 18 Jan 2022 10:44:34 +0000 Subject: [PATCH 121/460] Remove unnecessary minimum_version_id_old fields The migration code will not look at a VMStateDescription's minimum_version_id_old field unless that VMSD has set the load_state_old field to something non-NULL. (The purpose of minimum_version_id_old is to specify what migration version is needed for the code in the function pointed to by load_state_old to be able to handle it on incoming migration.) We have exactly one VMSD which still has a load_state_old, in the PPC CPU; every other VMSD which sets minimum_version_id_old is doing so unnecessarily. Delete all the unnecessary ones. Commit created with: sed -i '/\.minimum_version_id_old/d' $(git grep -l '\.minimum_version_id_old') with the one legitimate use then hand-edited back in. Signed-off-by: Peter Maydell Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela --- It missed vmstate_ppc_cpu. --- hw/acpi/cpu.c | 2 -- hw/acpi/ich9.c | 3 --- hw/acpi/memory_hotplug.c | 2 -- hw/acpi/piix4.c | 2 -- hw/acpi/tco.c | 1 - hw/audio/pcspk.c | 1 - hw/display/macfb.c | 1 - hw/dma/xlnx-zdma.c | 1 - hw/dma/xlnx_csu_dma.c | 1 - hw/gpio/imx_gpio.c | 1 - hw/misc/bcm2835_mbox.c | 1 - hw/net/can/can_kvaser_pci.c | 1 - hw/net/can/can_mioe3680_pci.c | 1 - hw/net/can/can_pcm3680_pci.c | 1 - hw/net/can/can_sja1000.c | 2 -- hw/net/can/ctucan_core.c | 2 -- hw/net/can/ctucan_pci.c | 1 - hw/ppc/ppc.c | 1 - hw/scsi/megasas.c | 1 - hw/scsi/mptsas.c | 1 - hw/virtio/virtio-mmio.c | 1 - hw/virtio/virtio-pci.c | 1 - hw/virtio/virtio.c | 1 - target/openrisc/machine.c | 1 - target/ppc/machine.c | 2 -- target/sparc/machine.c | 4 ---- 26 files changed, 37 deletions(-) diff --git a/hw/acpi/cpu.c b/hw/acpi/cpu.c index b20903ea30..3646dbfe68 100644 --- a/hw/acpi/cpu.c +++ b/hw/acpi/cpu.c @@ -297,7 +297,6 @@ static const VMStateDescription vmstate_cpuhp_sts = { .name = "CPU hotplug device state", .version_id = 1, .minimum_version_id = 1, - .minimum_version_id_old = 1, .fields = (VMStateField[]) { VMSTATE_BOOL(is_inserting, AcpiCpuStatus), VMSTATE_BOOL(is_removing, AcpiCpuStatus), @@ -311,7 +310,6 @@ const VMStateDescription vmstate_cpu_hotplug = { .name = "CPU hotplug state", .version_id = 1, .minimum_version_id = 1, - .minimum_version_id_old = 1, .fields = (VMStateField[]) { VMSTATE_UINT32(selector, CPUHotplugState), VMSTATE_UINT8(command, CPUHotplugState), diff --git a/hw/acpi/ich9.c b/hw/acpi/ich9.c index ebe08ed831..bd9bbade70 100644 --- a/hw/acpi/ich9.c +++ b/hw/acpi/ich9.c @@ -163,7 +163,6 @@ static const VMStateDescription vmstate_memhp_state = { .name = "ich9_pm/memhp", .version_id = 1, .minimum_version_id = 1, - .minimum_version_id_old = 1, .needed = vmstate_test_use_memhp, .fields = (VMStateField[]) { VMSTATE_MEMORY_HOTPLUG(acpi_memory_hotplug, ICH9LPCPMRegs), @@ -181,7 +180,6 @@ static const VMStateDescription vmstate_tco_io_state = { .name = "ich9_pm/tco", .version_id = 1, .minimum_version_id = 1, - .minimum_version_id_old = 1, .needed = vmstate_test_use_tco, .fields = (VMStateField[]) { VMSTATE_STRUCT(tco_regs, ICH9LPCPMRegs, 1, vmstate_tco_io_sts, @@ -208,7 +206,6 @@ static const VMStateDescription vmstate_cpuhp_state = { .name = "ich9_pm/cpuhp", .version_id = 1, .minimum_version_id = 1, - .minimum_version_id_old = 1, .needed = vmstate_test_use_cpuhp, .pre_load = vmstate_cpuhp_pre_load, .fields = (VMStateField[]) { diff --git a/hw/acpi/memory_hotplug.c b/hw/acpi/memory_hotplug.c index d0fffcf787..a581a2183b 100644 --- a/hw/acpi/memory_hotplug.c +++ b/hw/acpi/memory_hotplug.c @@ -318,7 +318,6 @@ static const VMStateDescription vmstate_memhp_sts = { .name = "memory hotplug device state", .version_id = 1, .minimum_version_id = 1, - .minimum_version_id_old = 1, .fields = (VMStateField[]) { VMSTATE_BOOL(is_enabled, MemStatus), VMSTATE_BOOL(is_inserting, MemStatus), @@ -332,7 +331,6 @@ const VMStateDescription vmstate_memory_hotplug = { .name = "memory hotplug state", .version_id = 1, .minimum_version_id = 1, - .minimum_version_id_old = 1, .fields = (VMStateField[]) { VMSTATE_UINT32(selector, MemHotplugState), VMSTATE_STRUCT_VARRAY_POINTER_UINT32(devs, MemHotplugState, dev_count, diff --git a/hw/acpi/piix4.c b/hw/acpi/piix4.c index f0b5fac44a..cc37fa3416 100644 --- a/hw/acpi/piix4.c +++ b/hw/acpi/piix4.c @@ -230,7 +230,6 @@ static const VMStateDescription vmstate_memhp_state = { .name = "piix4_pm/memhp", .version_id = 1, .minimum_version_id = 1, - .minimum_version_id_old = 1, .needed = vmstate_test_use_memhp, .fields = (VMStateField[]) { VMSTATE_MEMORY_HOTPLUG(acpi_memory_hotplug, PIIX4PMState), @@ -255,7 +254,6 @@ static const VMStateDescription vmstate_cpuhp_state = { .name = "piix4_pm/cpuhp", .version_id = 1, .minimum_version_id = 1, - .minimum_version_id_old = 1, .needed = vmstate_test_use_cpuhp, .pre_load = vmstate_cpuhp_pre_load, .fields = (VMStateField[]) { diff --git a/hw/acpi/tco.c b/hw/acpi/tco.c index cf1e68a539..4783721e4e 100644 --- a/hw/acpi/tco.c +++ b/hw/acpi/tco.c @@ -239,7 +239,6 @@ const VMStateDescription vmstate_tco_io_sts = { .name = "tco io device status", .version_id = 1, .minimum_version_id = 1, - .minimum_version_id_old = 1, .fields = (VMStateField[]) { VMSTATE_UINT16(tco.rld, TCOIORegs), VMSTATE_UINT8(tco.din, TCOIORegs), diff --git a/hw/audio/pcspk.c b/hw/audio/pcspk.c index b056c05387..dfc7ebca4e 100644 --- a/hw/audio/pcspk.c +++ b/hw/audio/pcspk.c @@ -209,7 +209,6 @@ static const VMStateDescription vmstate_spk = { .name = "pcspk", .version_id = 1, .minimum_version_id = 1, - .minimum_version_id_old = 1, .needed = migrate_needed, .fields = (VMStateField[]) { VMSTATE_UINT8(data_on, PCSpkState), diff --git a/hw/display/macfb.c b/hw/display/macfb.c index 4bd7c3ad6a..2eeb80cc3f 100644 --- a/hw/display/macfb.c +++ b/hw/display/macfb.c @@ -616,7 +616,6 @@ static const VMStateDescription vmstate_macfb = { .name = "macfb", .version_id = 1, .minimum_version_id = 1, - .minimum_version_id_old = 1, .post_load = macfb_post_load, .fields = (VMStateField[]) { VMSTATE_UINT8_ARRAY(color_palette, MacfbState, 256 * 3), diff --git a/hw/dma/xlnx-zdma.c b/hw/dma/xlnx-zdma.c index a5a92b4ff8..4eb7f66e9f 100644 --- a/hw/dma/xlnx-zdma.c +++ b/hw/dma/xlnx-zdma.c @@ -806,7 +806,6 @@ static const VMStateDescription vmstate_zdma = { .name = TYPE_XLNX_ZDMA, .version_id = 1, .minimum_version_id = 1, - .minimum_version_id_old = 1, .fields = (VMStateField[]) { VMSTATE_UINT32_ARRAY(regs, XlnxZDMA, ZDMA_R_MAX), VMSTATE_UINT32(state, XlnxZDMA), diff --git a/hw/dma/xlnx_csu_dma.c b/hw/dma/xlnx_csu_dma.c index 896bb3574d..5b62a2f74f 100644 --- a/hw/dma/xlnx_csu_dma.c +++ b/hw/dma/xlnx_csu_dma.c @@ -663,7 +663,6 @@ static const VMStateDescription vmstate_xlnx_csu_dma = { .name = TYPE_XLNX_CSU_DMA, .version_id = 0, .minimum_version_id = 0, - .minimum_version_id_old = 0, .fields = (VMStateField[]) { VMSTATE_PTIMER(src_timer, XlnxCSUDMA), VMSTATE_UINT16(width, XlnxCSUDMA), diff --git a/hw/gpio/imx_gpio.c b/hw/gpio/imx_gpio.c index 7a591804a9..c7f98b7bb1 100644 --- a/hw/gpio/imx_gpio.c +++ b/hw/gpio/imx_gpio.c @@ -277,7 +277,6 @@ static const VMStateDescription vmstate_imx_gpio = { .name = TYPE_IMX_GPIO, .version_id = 1, .minimum_version_id = 1, - .minimum_version_id_old = 1, .fields = (VMStateField[]) { VMSTATE_UINT32(dr, IMXGPIOState), VMSTATE_UINT32(gdir, IMXGPIOState), diff --git a/hw/misc/bcm2835_mbox.c b/hw/misc/bcm2835_mbox.c index 9f73cbd5e4..04e53c9828 100644 --- a/hw/misc/bcm2835_mbox.c +++ b/hw/misc/bcm2835_mbox.c @@ -271,7 +271,6 @@ static const VMStateDescription vmstate_bcm2835_mbox = { .name = TYPE_BCM2835_MBOX, .version_id = 1, .minimum_version_id = 1, - .minimum_version_id_old = 1, .fields = (VMStateField[]) { VMSTATE_BOOL_ARRAY(available, BCM2835MboxState, MBOX_CHAN_COUNT), VMSTATE_STRUCT_ARRAY(mbox, BCM2835MboxState, 2, 1, diff --git a/hw/net/can/can_kvaser_pci.c b/hw/net/can/can_kvaser_pci.c index 168b3a620d..94b3a534f8 100644 --- a/hw/net/can/can_kvaser_pci.c +++ b/hw/net/can/can_kvaser_pci.c @@ -266,7 +266,6 @@ static const VMStateDescription vmstate_kvaser_pci = { .name = "kvaser_pci", .version_id = 1, .minimum_version_id = 1, - .minimum_version_id_old = 1, .fields = (VMStateField[]) { VMSTATE_PCI_DEVICE(dev, KvaserPCIState), /* Load this before sja_state. */ diff --git a/hw/net/can/can_mioe3680_pci.c b/hw/net/can/can_mioe3680_pci.c index 7a79e2605a..29dc696f7c 100644 --- a/hw/net/can/can_mioe3680_pci.c +++ b/hw/net/can/can_mioe3680_pci.c @@ -203,7 +203,6 @@ static const VMStateDescription vmstate_mioe3680_pci = { .name = "mioe3680_pci", .version_id = 1, .minimum_version_id = 1, - .minimum_version_id_old = 1, .fields = (VMStateField[]) { VMSTATE_PCI_DEVICE(dev, Mioe3680PCIState), VMSTATE_STRUCT(sja_state[0], Mioe3680PCIState, 0, vmstate_can_sja, diff --git a/hw/net/can/can_pcm3680_pci.c b/hw/net/can/can_pcm3680_pci.c index 8ef4e74af0..e8e57f4f33 100644 --- a/hw/net/can/can_pcm3680_pci.c +++ b/hw/net/can/can_pcm3680_pci.c @@ -204,7 +204,6 @@ static const VMStateDescription vmstate_pcm3680i_pci = { .name = "pcm3680i_pci", .version_id = 1, .minimum_version_id = 1, - .minimum_version_id_old = 1, .fields = (VMStateField[]) { VMSTATE_PCI_DEVICE(dev, Pcm3680iPCIState), VMSTATE_STRUCT(sja_state[0], Pcm3680iPCIState, 0, diff --git a/hw/net/can/can_sja1000.c b/hw/net/can/can_sja1000.c index 34eea684ce..3ba803e947 100644 --- a/hw/net/can/can_sja1000.c +++ b/hw/net/can/can_sja1000.c @@ -928,7 +928,6 @@ const VMStateDescription vmstate_qemu_can_filter = { .name = "qemu_can_filter", .version_id = 1, .minimum_version_id = 1, - .minimum_version_id_old = 1, .fields = (VMStateField[]) { VMSTATE_UINT32(can_id, qemu_can_filter), VMSTATE_UINT32(can_mask, qemu_can_filter), @@ -952,7 +951,6 @@ const VMStateDescription vmstate_can_sja = { .name = "can_sja", .version_id = 1, .minimum_version_id = 1, - .minimum_version_id_old = 1, .post_load = can_sja_post_load, .fields = (VMStateField[]) { VMSTATE_UINT8(mode, CanSJA1000State), diff --git a/hw/net/can/ctucan_core.c b/hw/net/can/ctucan_core.c index d171c372e0..f2c3b6a706 100644 --- a/hw/net/can/ctucan_core.c +++ b/hw/net/can/ctucan_core.c @@ -617,7 +617,6 @@ const VMStateDescription vmstate_qemu_ctucan_tx_buffer = { .name = "qemu_ctucan_tx_buffer", .version_id = 1, .minimum_version_id = 1, - .minimum_version_id_old = 1, .fields = (VMStateField[]) { VMSTATE_UINT8_ARRAY(data, CtuCanCoreMsgBuffer, CTUCAN_CORE_MSG_MAX_LEN), VMSTATE_END_OF_LIST() @@ -636,7 +635,6 @@ const VMStateDescription vmstate_ctucan = { .name = "ctucan", .version_id = 1, .minimum_version_id = 1, - .minimum_version_id_old = 1, .post_load = ctucan_post_load, .fields = (VMStateField[]) { VMSTATE_UINT32(mode_settings.u32, CtuCanCoreState), diff --git a/hw/net/can/ctucan_pci.c b/hw/net/can/ctucan_pci.c index f1c86cd06a..50f4ea6cd6 100644 --- a/hw/net/can/ctucan_pci.c +++ b/hw/net/can/ctucan_pci.c @@ -215,7 +215,6 @@ static const VMStateDescription vmstate_ctucan_pci = { .name = "ctucan_pci", .version_id = 1, .minimum_version_id = 1, - .minimum_version_id_old = 1, .fields = (VMStateField[]) { VMSTATE_PCI_DEVICE(dev, CtuCanPCIState), VMSTATE_STRUCT(ctucan_state[0], CtuCanPCIState, 0, vmstate_ctucan, diff --git a/hw/ppc/ppc.c b/hw/ppc/ppc.c index bb5bee9a33..462c87dba8 100644 --- a/hw/ppc/ppc.c +++ b/hw/ppc/ppc.c @@ -1049,7 +1049,6 @@ const VMStateDescription vmstate_ppc_timebase = { .name = "timebase", .version_id = 1, .minimum_version_id = 1, - .minimum_version_id_old = 1, .pre_save = timebase_pre_save, .fields = (VMStateField []) { VMSTATE_UINT64(guest_timebase, PPCTimebase), diff --git a/hw/scsi/megasas.c b/hw/scsi/megasas.c index c9da5ce0b5..203f25d4c4 100644 --- a/hw/scsi/megasas.c +++ b/hw/scsi/megasas.c @@ -2315,7 +2315,6 @@ static const VMStateDescription vmstate_megasas_gen2 = { .name = "megasas-gen2", .version_id = 0, .minimum_version_id = 0, - .minimum_version_id_old = 0, .fields = (VMStateField[]) { VMSTATE_PCI_DEVICE(parent_obj, MegasasState), VMSTATE_MSIX(parent_obj, MegasasState), diff --git a/hw/scsi/mptsas.c b/hw/scsi/mptsas.c index 5181b0c0b0..706cf0df3a 100644 --- a/hw/scsi/mptsas.c +++ b/hw/scsi/mptsas.c @@ -1363,7 +1363,6 @@ static const VMStateDescription vmstate_mptsas = { .name = "mptsas", .version_id = 0, .minimum_version_id = 0, - .minimum_version_id_old = 0, .post_load = mptsas_post_load, .fields = (VMStateField[]) { VMSTATE_PCI_DEVICE(dev, MPTSASState), diff --git a/hw/virtio/virtio-mmio.c b/hw/virtio/virtio-mmio.c index 72da12fea5..688eccda94 100644 --- a/hw/virtio/virtio-mmio.c +++ b/hw/virtio/virtio-mmio.c @@ -592,7 +592,6 @@ static const VMStateDescription vmstate_virtio_mmio = { .name = "virtio_mmio", .version_id = 1, .minimum_version_id = 1, - .minimum_version_id_old = 1, .fields = (VMStateField[]) { VMSTATE_END_OF_LIST() }, diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index 750aa47ec1..f9cf9592fd 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -131,7 +131,6 @@ static const VMStateDescription vmstate_virtio_pci = { .name = "virtio_pci", .version_id = 1, .minimum_version_id = 1, - .minimum_version_id_old = 1, .fields = (VMStateField[]) { VMSTATE_END_OF_LIST() }, diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index aae72fb8b7..9e8f51dfb0 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -2808,7 +2808,6 @@ static const VMStateDescription vmstate_virtio = { .name = "virtio", .version_id = 1, .minimum_version_id = 1, - .minimum_version_id_old = 1, .fields = (VMStateField[]) { VMSTATE_END_OF_LIST() }, diff --git a/target/openrisc/machine.c b/target/openrisc/machine.c index 6239725c4f..b7d7388640 100644 --- a/target/openrisc/machine.c +++ b/target/openrisc/machine.c @@ -25,7 +25,6 @@ static const VMStateDescription vmstate_tlb_entry = { .name = "tlb_entry", .version_id = 1, .minimum_version_id = 1, - .minimum_version_id_old = 1, .fields = (VMStateField[]) { VMSTATE_UINTTL(mr, OpenRISCTLBEntry), VMSTATE_UINTTL(tr, OpenRISCTLBEntry), diff --git a/target/ppc/machine.c b/target/ppc/machine.c index 733a22d744..a503e00ddc 100644 --- a/target/ppc/machine.c +++ b/target/ppc/machine.c @@ -421,7 +421,6 @@ static const VMStateDescription vmstate_tm = { .name = "cpu/tm", .version_id = 1, .minimum_version_id = 1, - .minimum_version_id_old = 1, .needed = tm_needed, .fields = (VMStateField []) { VMSTATE_UINTTL_ARRAY(env.tm_gpr, PowerPCCPU, 32), @@ -672,7 +671,6 @@ const VMStateDescription vmstate_ppc_cpu = { .name = "cpu", .version_id = 5, .minimum_version_id = 5, - .minimum_version_id_old = 4, .pre_save = cpu_pre_save, .post_load = cpu_post_load, .fields = (VMStateField[]) { diff --git a/target/sparc/machine.c b/target/sparc/machine.c index 917375c3a1..44b9e7d75d 100644 --- a/target/sparc/machine.c +++ b/target/sparc/machine.c @@ -10,7 +10,6 @@ static const VMStateDescription vmstate_cpu_timer = { .name = "cpu_timer", .version_id = 1, .minimum_version_id = 1, - .minimum_version_id_old = 1, .fields = (VMStateField[]) { VMSTATE_UINT32(frequency, CPUTimer), VMSTATE_UINT32(disabled, CPUTimer), @@ -30,7 +29,6 @@ static const VMStateDescription vmstate_trap_state = { .name = "trap_state", .version_id = 1, .minimum_version_id = 1, - .minimum_version_id_old = 1, .fields = (VMStateField[]) { VMSTATE_UINT64(tpc, trap_state), VMSTATE_UINT64(tnpc, trap_state), @@ -44,7 +42,6 @@ static const VMStateDescription vmstate_tlb_entry = { .name = "tlb_entry", .version_id = 1, .minimum_version_id = 1, - .minimum_version_id_old = 1, .fields = (VMStateField[]) { VMSTATE_UINT64(tag, SparcTLBEntry), VMSTATE_UINT64(tte, SparcTLBEntry), @@ -113,7 +110,6 @@ const VMStateDescription vmstate_sparc_cpu = { .name = "cpu", .version_id = SPARC_VMSTATE_VER, .minimum_version_id = SPARC_VMSTATE_VER, - .minimum_version_id_old = SPARC_VMSTATE_VER, .pre_save = cpu_pre_save, .fields = (VMStateField[]) { VMSTATE_UINTTL_ARRAY(env.gregs, SPARCCPU, 8), From 444252b96a63fef6537af0a43665905b48816927 Mon Sep 17 00:00:00 2001 From: Zhang Chen Date: Fri, 31 Dec 2021 13:59:33 +0800 Subject: [PATCH 122/460] migration/migration.c: Add missed default error handler for migration state In the migration_completion() no other status is expected, for example MIGRATION_STATUS_CANCELLING, MIGRATION_STATUS_CANCELLED, etc. Signed-off-by: Zhang Chen Reviewed-by: Dr. David Alan Gilbert Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela --- migration/migration.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/migration/migration.c b/migration/migration.c index 0652165610..2afa77da03 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -3205,7 +3205,7 @@ static void migration_completion(MigrationState *s) qemu_mutex_unlock_iothread(); trace_migration_completion_postcopy_end_after_complete(); - } else if (s->state == MIGRATION_STATUS_CANCELLING) { + } else { goto fail; } From eeeb48ee3389e837428370a8ed2772c2e74cce49 Mon Sep 17 00:00:00 2001 From: Zhang Chen Date: Fri, 31 Dec 2021 13:59:34 +0800 Subject: [PATCH 123/460] migration/migration.c: Avoid COLO boot in postcopy migration COLO dose not support postcopy migration and remove the Fixme. Signed-off-by: Zhang Chen Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela --- migration/migration.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index 2afa77da03..5b2e3c66d1 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -3230,7 +3230,11 @@ static void migration_completion(MigrationState *s) goto fail_invalidate; } - if (!migrate_colo_enabled()) { + if (migrate_colo_enabled() && s->state == MIGRATION_STATUS_ACTIVE) { + /* COLO does not support postcopy */ + migrate_set_state(&s->state, MIGRATION_STATUS_ACTIVE, + MIGRATION_STATUS_COLO); + } else { migrate_set_state(&s->state, current_active_state, MIGRATION_STATUS_COMPLETED); } @@ -3621,10 +3625,6 @@ static void migration_iteration_finish(MigrationState *s) "COLO enabled", __func__); } migrate_start_colo_process(s); - /* - * Fixme: we will run VM in COLO no matter its old running state. - * After exited COLO, we will keep running. - */ /* Fallthrough */ case MIGRATION_STATUS_ACTIVE: /* From 01ee5e355620ab1cc356964472f89cfb89cddc6d Mon Sep 17 00:00:00 2001 From: Zhang Chen Date: Fri, 31 Dec 2021 13:59:35 +0800 Subject: [PATCH 124/460] migration/migration.c: Remove the MIGRATION_STATUS_ACTIVE when migration finished The MIGRATION_STATUS_ACTIVE indicates that migration is running. Remove it to be handled by the default operation, It should be part of the unknown ending states. Signed-off-by: Zhang Chen Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela --- migration/migration.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index 5b2e3c66d1..3849b33108 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -3625,12 +3625,6 @@ static void migration_iteration_finish(MigrationState *s) "COLO enabled", __func__); } migrate_start_colo_process(s); - /* Fallthrough */ - case MIGRATION_STATUS_ACTIVE: - /* - * We should really assert here, but since it's during - * migration, let's try to reduce the usage of assertions. - */ s->vm_was_running = true; /* Fallthrough */ case MIGRATION_STATUS_FAILED: From 9200265838e5e6f43b1acdafb610d0a813b801c1 Mon Sep 17 00:00:00 2001 From: David Edmondson Date: Wed, 15 Dec 2021 14:14:37 +0000 Subject: [PATCH 125/460] migration: Report the error returned when save_live_iterate fails Should qemu_savevm_state_iterate() encounter a failure when calling a particular save_live_iterate function, report the error code returned by the function. Signed-off-by: David Edmondson Reviewed-by: Dr. David Alan Gilbert Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela --- migration/savevm.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/migration/savevm.c b/migration/savevm.c index 0bef031acb..1599b02fbc 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -1298,8 +1298,9 @@ int qemu_savevm_state_iterate(QEMUFile *f, bool postcopy) save_section_footer(f, se); if (ret < 0) { - error_report("failed to save SaveStateEntry with id(name): %d(%s)", - se->section_id, se->idstr); + error_report("failed to save SaveStateEntry with id(name): " + "%d(%s): %d", + se->section_id, se->idstr, ret); qemu_file_set_error(f, ret); } if (ret <= 0) { From a6d1223b4ae47e4d4642fce2ec504484a1355723 Mon Sep 17 00:00:00 2001 From: Xu Zheng Date: Mon, 17 Jan 2022 10:30:03 +0800 Subject: [PATCH 126/460] migration/ram: clean up unused comment. Just a removal of an unused comment. a0a8aa147aa did many fixes and removed the parameter named "ms", but forget to remove the corresponding comment in function named "ram_save_host_page". Signed-off-by: Xu Zheng Signed-off-by: Mao Zhongyi Signed-off-by: Juan Quintela Reviewed-by: Juan Quintela --- migration/ram.c | 1 - 1 file changed, 1 deletion(-) diff --git a/migration/ram.c b/migration/ram.c index e9dcd3ca4e..b4477b8ee0 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -2179,7 +2179,6 @@ static int ram_save_target_page(RAMState *rs, PageSearchStatus *pss) * Returns the number of pages written or negative on error * * @rs: current RAM state - * @ms: current migration state * @pss: data about the page we want to send */ static int ram_save_host_page(RAMState *rs, PageSearchStatus *pss) From 53405ffb33e31c32725d7c463f07074d4f622677 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Tue, 7 Dec 2021 19:50:10 +0800 Subject: [PATCH 127/460] migration: Drop dead code of ram_debug_dump_bitmap() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I planned to add "#ifdef DEBUG_POSTCOPY" around the function too because otherwise it'll be compiled into qemu binary even if it'll never be used. Then I found that maybe it's easier to just drop it for good.. Signed-off-by: Peter Xu Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Dr. David Alan Gilbert Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela --- migration/ram.c | 39 --------------------------------------- migration/ram.h | 2 -- 2 files changed, 41 deletions(-) diff --git a/migration/ram.c b/migration/ram.c index b4477b8ee0..9a06ea6dd4 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -2394,40 +2394,6 @@ static void ram_state_reset(RAMState *rs) #define MAX_WAIT 50 /* ms, half buffered_file limit */ -/* - * 'expected' is the value you expect the bitmap mostly to be full - * of; it won't bother printing lines that are all this value. - * If 'todump' is null the migration bitmap is dumped. - */ -void ram_debug_dump_bitmap(unsigned long *todump, bool expected, - unsigned long pages) -{ - int64_t cur; - int64_t linelen = 128; - char linebuf[129]; - - for (cur = 0; cur < pages; cur += linelen) { - int64_t curb; - bool found = false; - /* - * Last line; catch the case where the line length - * is longer than remaining ram - */ - if (cur + linelen > pages) { - linelen = pages - cur; - } - for (curb = 0; curb < linelen; curb++) { - bool thisbit = test_bit(cur + curb, todump); - linebuf[curb] = thisbit ? '1' : '.'; - found = found || (thisbit != expected); - } - if (found) { - linebuf[curb] = '\0'; - fprintf(stderr, "0x%08" PRIx64 " : %s\n", cur, linebuf); - } - } -} - /* **** functions for postcopy ***** */ void ram_postcopy_migrated_memory_release(MigrationState *ms) @@ -2655,11 +2621,6 @@ int ram_postcopy_send_discard_bitmap(MigrationState *ms) if (ret) { return ret; } - -#ifdef DEBUG_POSTCOPY - ram_debug_dump_bitmap(block->bmap, true, - block->used_length >> TARGET_PAGE_BITS); -#endif } trace_ram_postcopy_send_discard_bitmap(); diff --git a/migration/ram.h b/migration/ram.h index c515396a9a..f543e25765 100644 --- a/migration/ram.h +++ b/migration/ram.h @@ -55,8 +55,6 @@ void mig_throttle_counter_reset(void); uint64_t ram_pagesize_summary(void); int ram_save_queue_pages(const char *rbname, ram_addr_t start, ram_addr_t len); void acct_update_position(QEMUFile *f, size_t size, bool zero); -void ram_debug_dump_bitmap(unsigned long *todump, bool expected, - unsigned long pages); void ram_postcopy_migrated_memory_release(MigrationState *ms); /* For outgoing discard bitmap */ int ram_postcopy_send_discard_bitmap(MigrationState *ms); From dc57d6f2ec3eb9ba6d8a6cac735bf6d02017dda2 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Tue, 7 Dec 2021 19:50:11 +0800 Subject: [PATCH 128/460] migration: Don't return for postcopy_chunk_hostpages() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It always return zero, because it just can't go wrong so far. Simplify the code with no functional change. Signed-off-by: Peter Xu Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Dr. David Alan Gilbert Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela --- migration/ram.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/migration/ram.c b/migration/ram.c index 9a06ea6dd4..c60cf5ad83 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -2566,12 +2566,10 @@ static void postcopy_chunk_hostpages_pass(MigrationState *ms, RAMBlock *block) * dirty host-page size chunks as all dirty. In this case the host-page * is the host-page for the particular RAMBlock, i.e. it might be a huge page * - * Returns zero on success - * * @ms: current migration state * @block: block we want to work with */ -static int postcopy_chunk_hostpages(MigrationState *ms, RAMBlock *block) +static void postcopy_chunk_hostpages(MigrationState *ms, RAMBlock *block) { postcopy_discard_send_init(ms, block->idstr); @@ -2581,7 +2579,6 @@ static int postcopy_chunk_hostpages(MigrationState *ms, RAMBlock *block) postcopy_chunk_hostpages_pass(ms, block); postcopy_discard_send_finish(ms); - return 0; } /** @@ -2603,7 +2600,6 @@ int ram_postcopy_send_discard_bitmap(MigrationState *ms) { RAMState *rs = ram_state; RAMBlock *block; - int ret; RCU_READ_LOCK_GUARD(); @@ -2617,10 +2613,7 @@ int ram_postcopy_send_discard_bitmap(MigrationState *ms) RAMBLOCK_FOREACH_NOT_IGNORED(block) { /* Deal with TPS != HPS and huge pages */ - ret = postcopy_chunk_hostpages(ms, block); - if (ret) { - return ret; - } + postcopy_chunk_hostpages(ms, block); } trace_ram_postcopy_send_discard_bitmap(); From e3fbf760218fe06d0c95bb595774c76206219e2f Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Tue, 7 Dec 2021 19:50:12 +0800 Subject: [PATCH 129/460] migration: Drop postcopy_chunk_hostpages() This function calls three functions: - postcopy_discard_send_init(ms, block->idstr); - postcopy_chunk_hostpages_pass(ms, block); - postcopy_discard_send_finish(ms); However only the 2nd function call is meaningful. It's major role is to make sure dirty bits are applied in host-page-size granule, so there will be no partial dirty bits set for a whole host page if huge pages are used. The 1st/3rd call are for latter when we want to send the disgard ranges. They're mostly no-op here besides some tracepoints (which are misleading!). Drop them, then we can directly drop postcopy_chunk_hostpages() as a whole because we can call postcopy_chunk_hostpages_pass() directly. There're still some nice comments above postcopy_chunk_hostpages() that explain what it does. Copy it over to the caller's site. Signed-off-by: Peter Xu Reviewed-by: Dr. David Alan Gilbert Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela --- migration/ram.c | 33 +++++++-------------------------- 1 file changed, 7 insertions(+), 26 deletions(-) diff --git a/migration/ram.c b/migration/ram.c index c60cf5ad83..91d32002e8 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -2557,30 +2557,6 @@ static void postcopy_chunk_hostpages_pass(MigrationState *ms, RAMBlock *block) } } -/** - * postcopy_chunk_hostpages: discard any partially sent host page - * - * Utility for the outgoing postcopy code. - * - * Discard any partially sent host-page size chunks, mark any partially - * dirty host-page size chunks as all dirty. In this case the host-page - * is the host-page for the particular RAMBlock, i.e. it might be a huge page - * - * @ms: current migration state - * @block: block we want to work with - */ -static void postcopy_chunk_hostpages(MigrationState *ms, RAMBlock *block) -{ - postcopy_discard_send_init(ms, block->idstr); - - /* - * Ensure that all partially dirty host pages are made fully dirty. - */ - postcopy_chunk_hostpages_pass(ms, block); - - postcopy_discard_send_finish(ms); -} - /** * ram_postcopy_send_discard_bitmap: transmit the discard bitmap * @@ -2612,8 +2588,13 @@ int ram_postcopy_send_discard_bitmap(MigrationState *ms) rs->last_page = 0; RAMBLOCK_FOREACH_NOT_IGNORED(block) { - /* Deal with TPS != HPS and huge pages */ - postcopy_chunk_hostpages(ms, block); + /* + * Deal with TPS != HPS and huge pages. It discard any partially sent + * host-page size chunks, mark any partially dirty host-page size + * chunks as all dirty. In this case the host-page is the host-page + * for the particular RAMBlock, i.e. it might be a huge page. + */ + postcopy_chunk_hostpages_pass(ms, block); } trace_ram_postcopy_send_discard_bitmap(); From f30c2e5ba81b8d388b7beeae84e6db5ea8220924 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Tue, 7 Dec 2021 19:50:13 +0800 Subject: [PATCH 130/460] migration: Do chunk page in postcopy_each_ram_send_discard() Right now we loop ramblocks for twice, the 1st time chunk the dirty bits with huge page information; the 2nd time we send the discard ranges. That's not necessary - we can do them in a single loop. Signed-off-by: Peter Xu Reviewed-by: Dr. David Alan Gilbert Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela --- migration/ram.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/migration/ram.c b/migration/ram.c index 91d32002e8..d74a8c04b6 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -2454,6 +2454,8 @@ static int postcopy_send_discard_bm_ram(MigrationState *ms, RAMBlock *block) return 0; } +static void postcopy_chunk_hostpages_pass(MigrationState *ms, RAMBlock *block); + /** * postcopy_each_ram_send_discard: discard all RAMBlocks * @@ -2475,6 +2477,14 @@ static int postcopy_each_ram_send_discard(MigrationState *ms) RAMBLOCK_FOREACH_NOT_IGNORED(block) { postcopy_discard_send_init(ms, block->idstr); + /* + * Deal with TPS != HPS and huge pages. It discard any partially sent + * host-page size chunks, mark any partially dirty host-page size + * chunks as all dirty. In this case the host-page is the host-page + * for the particular RAMBlock, i.e. it might be a huge page. + */ + postcopy_chunk_hostpages_pass(ms, block); + /* * Postcopy sends chunks of bitmap over the wire, but it * just needs indexes at this point, avoids it having @@ -2575,7 +2585,6 @@ static void postcopy_chunk_hostpages_pass(MigrationState *ms, RAMBlock *block) int ram_postcopy_send_discard_bitmap(MigrationState *ms) { RAMState *rs = ram_state; - RAMBlock *block; RCU_READ_LOCK_GUARD(); @@ -2587,15 +2596,6 @@ int ram_postcopy_send_discard_bitmap(MigrationState *ms) rs->last_sent_block = NULL; rs->last_page = 0; - RAMBLOCK_FOREACH_NOT_IGNORED(block) { - /* - * Deal with TPS != HPS and huge pages. It discard any partially sent - * host-page size chunks, mark any partially dirty host-page size - * chunks as all dirty. In this case the host-page is the host-page - * for the particular RAMBlock, i.e. it might be a huge page. - */ - postcopy_chunk_hostpages_pass(ms, block); - } trace_ram_postcopy_send_discard_bitmap(); return postcopy_each_ram_send_discard(ms); From 739fcc1b0e4b04216f971c9fb87fb0e9ec599c34 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Tue, 7 Dec 2021 19:50:14 +0800 Subject: [PATCH 131/460] migration: Drop return code for disgard ram process MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It will just never fail. Drop those return values where they're constantly zeros. A tiny touch-up on the tracepoint so trace_ram_postcopy_send_discard_bitmap() is called after the logic itself (which sounds more reasonable). Signed-off-by: Peter Xu Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Dr. David Alan Gilbert Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela --- migration/migration.c | 5 +---- migration/ram.c | 20 +++++--------------- migration/ram.h | 2 +- 3 files changed, 7 insertions(+), 20 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index 3849b33108..771f3e2bec 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -2991,10 +2991,7 @@ static int postcopy_start(MigrationState *ms) * that are dirty */ if (migrate_postcopy_ram()) { - if (ram_postcopy_send_discard_bitmap(ms)) { - error_report("postcopy send discard bitmap failed"); - goto fail; - } + ram_postcopy_send_discard_bitmap(ms); } /* diff --git a/migration/ram.c b/migration/ram.c index d74a8c04b6..5489ee3b7a 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -2459,8 +2459,6 @@ static void postcopy_chunk_hostpages_pass(MigrationState *ms, RAMBlock *block); /** * postcopy_each_ram_send_discard: discard all RAMBlocks * - * Returns 0 for success or negative for error - * * Utility for the outgoing postcopy code. * Calls postcopy_send_discard_bm_ram for each RAMBlock * passing it bitmap indexes and name. @@ -2469,10 +2467,9 @@ static void postcopy_chunk_hostpages_pass(MigrationState *ms, RAMBlock *block); * * @ms: current migration state */ -static int postcopy_each_ram_send_discard(MigrationState *ms) +static void postcopy_each_ram_send_discard(MigrationState *ms) { struct RAMBlock *block; - int ret; RAMBLOCK_FOREACH_NOT_IGNORED(block) { postcopy_discard_send_init(ms, block->idstr); @@ -2490,14 +2487,9 @@ static int postcopy_each_ram_send_discard(MigrationState *ms) * just needs indexes at this point, avoids it having * target page specific code. */ - ret = postcopy_send_discard_bm_ram(ms, block); + postcopy_send_discard_bm_ram(ms, block); postcopy_discard_send_finish(ms); - if (ret) { - return ret; - } } - - return 0; } /** @@ -2570,8 +2562,6 @@ static void postcopy_chunk_hostpages_pass(MigrationState *ms, RAMBlock *block) /** * ram_postcopy_send_discard_bitmap: transmit the discard bitmap * - * Returns zero on success - * * Transmit the set of pages to be discarded after precopy to the target * these are pages that: * a) Have been previously transmitted but are now dirty again @@ -2582,7 +2572,7 @@ static void postcopy_chunk_hostpages_pass(MigrationState *ms, RAMBlock *block) * * @ms: current migration state */ -int ram_postcopy_send_discard_bitmap(MigrationState *ms) +void ram_postcopy_send_discard_bitmap(MigrationState *ms) { RAMState *rs = ram_state; @@ -2596,9 +2586,9 @@ int ram_postcopy_send_discard_bitmap(MigrationState *ms) rs->last_sent_block = NULL; rs->last_page = 0; - trace_ram_postcopy_send_discard_bitmap(); + postcopy_each_ram_send_discard(ms); - return postcopy_each_ram_send_discard(ms); + trace_ram_postcopy_send_discard_bitmap(); } /** diff --git a/migration/ram.h b/migration/ram.h index f543e25765..2c6dc3675d 100644 --- a/migration/ram.h +++ b/migration/ram.h @@ -57,7 +57,7 @@ int ram_save_queue_pages(const char *rbname, ram_addr_t start, ram_addr_t len); void acct_update_position(QEMUFile *f, size_t size, bool zero); void ram_postcopy_migrated_memory_release(MigrationState *ms); /* For outgoing discard bitmap */ -int ram_postcopy_send_discard_bitmap(MigrationState *ms); +void ram_postcopy_send_discard_bitmap(MigrationState *ms); /* For incoming postcopy discard */ int ram_discard_range(const char *block_name, uint64_t start, size_t length); int ram_postcopy_incoming_init(MigrationIncomingState *mis); From 9e7d1223ace8c1182e362de8894ebe8d111c8918 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 30 Dec 2021 17:05:25 +0100 Subject: [PATCH 132/460] migration: Don't return for postcopy_send_discard_bm_ram() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit postcopy_send_discard_bm_ram() always return zero. Since it can't fail, simplify and do not return anything. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: David Edmondson Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela --- migration/ram.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/migration/ram.c b/migration/ram.c index 5489ee3b7a..1771bbdb02 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -2419,14 +2419,12 @@ void ram_postcopy_migrated_memory_release(MigrationState *ms) /** * postcopy_send_discard_bm_ram: discard a RAMBlock * - * Returns zero on success - * * Callback from postcopy_each_ram_send_discard for each RAMBlock * * @ms: current migration state * @block: RAMBlock to discard */ -static int postcopy_send_discard_bm_ram(MigrationState *ms, RAMBlock *block) +static void postcopy_send_discard_bm_ram(MigrationState *ms, RAMBlock *block) { unsigned long end = block->used_length >> TARGET_PAGE_BITS; unsigned long current; @@ -2450,8 +2448,6 @@ static int postcopy_send_discard_bm_ram(MigrationState *ms, RAMBlock *block) postcopy_discard_send_range(ms, one, discard_length); current = one + discard_length; } - - return 0; } static void postcopy_chunk_hostpages_pass(MigrationState *ms, RAMBlock *block); From 4c2d0f6dca24f3396ab0718ad3f9f53cc53004df Mon Sep 17 00:00:00 2001 From: David Edmondson Date: Tue, 21 Dec 2021 09:34:40 +0000 Subject: [PATCH 133/460] migration: Introduce ram_transferred_add() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace direct manipulation of ram_counters.transferred with a function. Signed-off-by: David Edmondson Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela --- migration/ram.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/migration/ram.c b/migration/ram.c index 1771bbdb02..619a1d9a6b 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -387,6 +387,11 @@ uint64_t ram_bytes_remaining(void) MigrationStats ram_counters; +static void ram_transferred_add(uint64_t bytes) +{ + ram_counters.transferred += bytes; +} + /* used by the search for pages to send */ struct PageSearchStatus { /* Current block being searched */ @@ -767,7 +772,7 @@ static int save_xbzrle_page(RAMState *rs, uint8_t **current_data, * RAM_SAVE_FLAG_CONTINUE. */ xbzrle_counters.bytes += bytes_xbzrle - 8; - ram_counters.transferred += bytes_xbzrle; + ram_transferred_add(bytes_xbzrle); return 1; } @@ -1208,7 +1213,7 @@ static int save_zero_page(RAMState *rs, RAMBlock *block, ram_addr_t offset) if (len) { ram_counters.duplicate++; - ram_counters.transferred += len; + ram_transferred_add(len); return 1; } return -1; @@ -1235,7 +1240,7 @@ static bool control_save_page(RAMState *rs, RAMBlock *block, ram_addr_t offset, } if (bytes_xmit) { - ram_counters.transferred += bytes_xmit; + ram_transferred_add(bytes_xmit); *pages = 1; } @@ -1266,8 +1271,8 @@ static bool control_save_page(RAMState *rs, RAMBlock *block, ram_addr_t offset, static int save_normal_page(RAMState *rs, RAMBlock *block, ram_addr_t offset, uint8_t *buf, bool async) { - ram_counters.transferred += save_page_header(rs, rs->f, block, - offset | RAM_SAVE_FLAG_PAGE); + ram_transferred_add(save_page_header(rs, rs->f, block, + offset | RAM_SAVE_FLAG_PAGE)); if (async) { qemu_put_buffer_async(rs->f, buf, TARGET_PAGE_SIZE, migrate_release_ram() & @@ -1275,7 +1280,7 @@ static int save_normal_page(RAMState *rs, RAMBlock *block, ram_addr_t offset, } else { qemu_put_buffer(rs->f, buf, TARGET_PAGE_SIZE); } - ram_counters.transferred += TARGET_PAGE_SIZE; + ram_transferred_add(TARGET_PAGE_SIZE); ram_counters.normal++; return 1; } @@ -1367,7 +1372,7 @@ static bool do_compress_ram_page(QEMUFile *f, z_stream *stream, RAMBlock *block, static void update_compress_thread_counts(const CompressParam *param, int bytes_xmit) { - ram_counters.transferred += bytes_xmit; + ram_transferred_add(bytes_xmit); if (param->zero_page) { ram_counters.duplicate++; @@ -2284,7 +2289,7 @@ void acct_update_position(QEMUFile *f, size_t size, bool zero) ram_counters.duplicate += pages; } else { ram_counters.normal += pages; - ram_counters.transferred += size; + ram_transferred_add(size); qemu_update_position(f, size); } } @@ -3040,7 +3045,7 @@ out: multifd_send_sync_main(rs->f); qemu_put_be64(f, RAM_SAVE_FLAG_EOS); qemu_fflush(f); - ram_counters.transferred += 8; + ram_transferred_add(8); ret = qemu_file_get_error(f); } From ae6806688016711bb9ec7541266d76ab511c5e3b Mon Sep 17 00:00:00 2001 From: David Edmondson Date: Tue, 21 Dec 2021 09:34:41 +0000 Subject: [PATCH 134/460] migration: Tally pre-copy, downtime and post-copy bytes independently MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Provide information on the number of bytes copied in the pre-copy, downtime and post-copy phases of migration. Signed-off-by: David Edmondson Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela --- migration/migration.c | 3 +++ migration/ram.c | 7 +++++++ monitor/hmp-cmds.c | 12 ++++++++++++ qapi/migration.json | 13 ++++++++++++- 4 files changed, 34 insertions(+), 1 deletion(-) diff --git a/migration/migration.c b/migration/migration.c index 771f3e2bec..bcc385b94b 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -1014,6 +1014,9 @@ static void populate_ram_info(MigrationInfo *info, MigrationState *s) info->ram->page_size = page_size; info->ram->multifd_bytes = ram_counters.multifd_bytes; info->ram->pages_per_second = s->pages_per_second; + info->ram->precopy_bytes = ram_counters.precopy_bytes; + info->ram->downtime_bytes = ram_counters.downtime_bytes; + info->ram->postcopy_bytes = ram_counters.postcopy_bytes; if (migrate_use_xbzrle()) { info->has_xbzrle_cache = true; diff --git a/migration/ram.c b/migration/ram.c index 619a1d9a6b..ca879c744f 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -389,6 +389,13 @@ MigrationStats ram_counters; static void ram_transferred_add(uint64_t bytes) { + if (runstate_is_running()) { + ram_counters.precopy_bytes += bytes; + } else if (migration_in_postcopy()) { + ram_counters.postcopy_bytes += bytes; + } else { + ram_counters.downtime_bytes += bytes; + } ram_counters.transferred += bytes; } diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c index 2669156b28..8c384dc1b2 100644 --- a/monitor/hmp-cmds.c +++ b/monitor/hmp-cmds.c @@ -293,6 +293,18 @@ void hmp_info_migrate(Monitor *mon, const QDict *qdict) monitor_printf(mon, "postcopy request count: %" PRIu64 "\n", info->ram->postcopy_requests); } + if (info->ram->precopy_bytes) { + monitor_printf(mon, "precopy ram: %" PRIu64 " kbytes\n", + info->ram->precopy_bytes >> 10); + } + if (info->ram->downtime_bytes) { + monitor_printf(mon, "downtime ram: %" PRIu64 " kbytes\n", + info->ram->downtime_bytes >> 10); + } + if (info->ram->postcopy_bytes) { + monitor_printf(mon, "postcopy ram: %" PRIu64 " kbytes\n", + info->ram->postcopy_bytes >> 10); + } } if (info->has_disk) { diff --git a/qapi/migration.json b/qapi/migration.json index bbfd48cf0b..5975a0e104 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -46,6 +46,15 @@ # @pages-per-second: the number of memory pages transferred per second # (Since 4.0) # +# @precopy-bytes: The number of bytes sent in the pre-copy phase +# (since 7.0). +# +# @downtime-bytes: The number of bytes sent while the guest is paused +# (since 7.0). +# +# @postcopy-bytes: The number of bytes sent during the post-copy phase +# (since 7.0). +# # Since: 0.14 ## { 'struct': 'MigrationStats', @@ -54,7 +63,9 @@ 'normal-bytes': 'int', 'dirty-pages-rate' : 'int', 'mbps' : 'number', 'dirty-sync-count' : 'int', 'postcopy-requests' : 'int', 'page-size' : 'int', - 'multifd-bytes' : 'uint64', 'pages-per-second' : 'uint64' } } + 'multifd-bytes' : 'uint64', 'pages-per-second' : 'uint64', + 'precopy-bytes' : 'uint64', 'downtime-bytes' : 'uint64', + 'postcopy-bytes' : 'uint64' } } ## # @XBZRLECacheStats: From 258f5c9825a9fb77aeae8a9d8c0877e714c090e2 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Wed, 19 Jan 2022 16:09:15 +0800 Subject: [PATCH 135/460] migration: No off-by-one for pss->page update in host page size We used to do off-by-one fixup for pss->page when finished one host huge page transfer. That seems to be unnecesary at all. Drop it. Cc: Keqian Zhu Cc: Kunkun Jiang Cc: Andrey Gruzdev Signed-off-by: Peter Xu Reviewed-by: Dr. David Alan Gilbert Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela --- migration/ram.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/migration/ram.c b/migration/ram.c index ca879c744f..58adcaf430 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -1617,7 +1617,7 @@ static int ram_save_release_protection(RAMState *rs, PageSearchStatus *pss, /* Check if page is from UFFD-managed region. */ if (pss->block->flags & RAM_UF_WRITEPROTECT) { void *page_address = pss->block->host + (start_page << TARGET_PAGE_BITS); - uint64_t run_length = (pss->page - start_page + 1) << TARGET_PAGE_BITS; + uint64_t run_length = (pss->page - start_page) << TARGET_PAGE_BITS; /* Flush async buffers before un-protect. */ qemu_fflush(rs->f); @@ -2230,7 +2230,7 @@ static int ram_save_host_page(RAMState *rs, PageSearchStatus *pss) offset_in_ramblock(pss->block, ((ram_addr_t)pss->page) << TARGET_PAGE_BITS)); /* The offset we leave with is the min boundary of host page and block */ - pss->page = MIN(pss->page, hostpage_boundary) - 1; + pss->page = MIN(pss->page, hostpage_boundary); res = ram_save_release_protection(rs, pss, start_page); return (res < 0 ? res : pages); From 2d1c37c603ab3ed9ec1abfc4f4491b8680b23a77 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Wed, 19 Jan 2022 16:09:17 +0800 Subject: [PATCH 136/460] migration: Enable UFFD_FEATURE_THREAD_ID even without blocktime feat This patch allows us to read the tid even without blocktime feature enabled. It's useful when tracing postcopy fault thread on faulted pages to show thread id too with the address. Remove the comments - they're merely not helpful at all. Signed-off-by: Peter Xu Reviewed-by: Dr. David Alan Gilbert Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela --- migration/postcopy-ram.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/migration/postcopy-ram.c b/migration/postcopy-ram.c index d18b5d05b2..2176ed68a5 100644 --- a/migration/postcopy-ram.c +++ b/migration/postcopy-ram.c @@ -283,15 +283,13 @@ static bool ufd_check_and_apply(int ufd, MigrationIncomingState *mis) } #ifdef UFFD_FEATURE_THREAD_ID - if (migrate_postcopy_blocktime() && mis && - UFFD_FEATURE_THREAD_ID & supported_features) { - /* kernel supports that feature */ - /* don't create blocktime_context if it exists */ - if (!mis->blocktime_ctx) { - mis->blocktime_ctx = blocktime_context_new(); - } - + if (UFFD_FEATURE_THREAD_ID & supported_features) { asked_features |= UFFD_FEATURE_THREAD_ID; + if (migrate_postcopy_blocktime()) { + if (!mis->blocktime_ctx) { + mis->blocktime_ctx = blocktime_context_new(); + } + } } #endif From a1fe28df7547120bc3ac8bc4c3d1565d4cf7905e Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Wed, 19 Jan 2022 16:09:18 +0800 Subject: [PATCH 137/460] migration: Add postcopy_has_request() Add a helper to detect whether postcopy has pending request. Since at it, cleanup the code a bit, e.g. in unqueue_page() we shouldn't need to check it again on queue empty because we're the only one (besides cleanup code, which should never run during this process) that will take a request off the list, so the request list can only grow but not shrink under the hood. Signed-off-by: Peter Xu Reviewed-by: Dr. David Alan Gilbert Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela --- migration/ram.c | 43 +++++++++++++++++++++++++++---------------- 1 file changed, 27 insertions(+), 16 deletions(-) diff --git a/migration/ram.c b/migration/ram.c index 58adcaf430..eb9db4f777 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -355,6 +355,12 @@ static RAMState *ram_state; static NotifierWithReturnList precopy_notifier_list; +/* Whether postcopy has queued requests? */ +static bool postcopy_has_request(RAMState *rs) +{ + return !QSIMPLEQ_EMPTY_ATOMIC(&rs->src_page_requests); +} + void precopy_infrastructure_init(void) { notifier_with_return_list_init(&precopy_notifier_list); @@ -1539,28 +1545,33 @@ static bool find_dirty_block(RAMState *rs, PageSearchStatus *pss, bool *again) */ static RAMBlock *unqueue_page(RAMState *rs, ram_addr_t *offset) { + struct RAMSrcPageRequest *entry; RAMBlock *block = NULL; - if (QSIMPLEQ_EMPTY_ATOMIC(&rs->src_page_requests)) { + if (!postcopy_has_request(rs)) { return NULL; } QEMU_LOCK_GUARD(&rs->src_page_req_mutex); - if (!QSIMPLEQ_EMPTY(&rs->src_page_requests)) { - struct RAMSrcPageRequest *entry = - QSIMPLEQ_FIRST(&rs->src_page_requests); - block = entry->rb; - *offset = entry->offset; - if (entry->len > TARGET_PAGE_SIZE) { - entry->len -= TARGET_PAGE_SIZE; - entry->offset += TARGET_PAGE_SIZE; - } else { - memory_region_unref(block->mr); - QSIMPLEQ_REMOVE_HEAD(&rs->src_page_requests, next_req); - g_free(entry); - migration_consume_urgent_request(); - } + /* + * This should _never_ change even after we take the lock, because no one + * should be taking anything off the request list other than us. + */ + assert(postcopy_has_request(rs)); + + entry = QSIMPLEQ_FIRST(&rs->src_page_requests); + block = entry->rb; + *offset = entry->offset; + + if (entry->len > TARGET_PAGE_SIZE) { + entry->len -= TARGET_PAGE_SIZE; + entry->offset += TARGET_PAGE_SIZE; + } else { + memory_region_unref(block->mr); + QSIMPLEQ_REMOVE_HEAD(&rs->src_page_requests, next_req); + g_free(entry); + migration_consume_urgent_request(); } return block; @@ -2992,7 +3003,7 @@ static int ram_save_iterate(QEMUFile *f, void *opaque) t0 = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); i = 0; while ((ret = qemu_file_rate_limit(f)) == 0 || - !QSIMPLEQ_EMPTY(&rs->src_page_requests)) { + postcopy_has_request(rs)) { int pages; if (qemu_file_get_error(f)) { From cfd66f30fb0f735df06ff4220e5000290a43dad3 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Wed, 19 Jan 2022 16:09:19 +0800 Subject: [PATCH 138/460] migration: Simplify unqueue_page() This patch simplifies unqueue_page() on both sides of it (itself, and caller). Firstly, due to the fact that right after unqueue_page() returned true, we'll definitely send a huge page (see ram_save_huge_page() call - it will _never_ exit before finish sending that huge page), so unqueue_page() does not need to jump in small page size if huge page is enabled on the ramblock. IOW, it's destined that only the 1st 4K page will be valid, when unqueue the 2nd+ time we'll notice the whole huge page has already been sent anyway. Switching to operating on huge page reduces a lot of the loops of redundant unqueue_page(). Meanwhile, drop the dirty check. It's not helpful to call test_bit() every time to jump over clean pages, as ram_save_host_page() has already done so, while in a faster way (see commit ba1b7c812c ("migration/ram: Optimize ram_save_host_page()", 2021-05-13)). So that's not necessary too. Drop the two tracepoints along the way - based on above analysis it's very possible that no one is really using it.. Signed-off-by: Peter Xu Reviewed-by: Dr. David Alan Gilbert Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela --- migration/ram.c | 37 +++++++++++-------------------------- migration/trace-events | 3 +-- 2 files changed, 12 insertions(+), 28 deletions(-) diff --git a/migration/ram.c b/migration/ram.c index eb9db4f777..91ca743ac8 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -1547,6 +1547,7 @@ static RAMBlock *unqueue_page(RAMState *rs, ram_addr_t *offset) { struct RAMSrcPageRequest *entry; RAMBlock *block = NULL; + size_t page_size; if (!postcopy_has_request(rs)) { return NULL; @@ -1563,10 +1564,13 @@ static RAMBlock *unqueue_page(RAMState *rs, ram_addr_t *offset) entry = QSIMPLEQ_FIRST(&rs->src_page_requests); block = entry->rb; *offset = entry->offset; + page_size = qemu_ram_pagesize(block); + /* Each page request should only be multiple page size of the ramblock */ + assert((entry->len % page_size) == 0); - if (entry->len > TARGET_PAGE_SIZE) { - entry->len -= TARGET_PAGE_SIZE; - entry->offset += TARGET_PAGE_SIZE; + if (entry->len > page_size) { + entry->len -= page_size; + entry->offset += page_size; } else { memory_region_unref(block->mr); QSIMPLEQ_REMOVE_HEAD(&rs->src_page_requests, next_req); @@ -1574,6 +1578,9 @@ static RAMBlock *unqueue_page(RAMState *rs, ram_addr_t *offset) migration_consume_urgent_request(); } + trace_unqueue_page(block->idstr, *offset, + test_bit((*offset >> TARGET_PAGE_BITS), block->bmap)); + return block; } @@ -1948,30 +1955,8 @@ static bool get_queued_page(RAMState *rs, PageSearchStatus *pss) { RAMBlock *block; ram_addr_t offset; - bool dirty; - do { - block = unqueue_page(rs, &offset); - /* - * We're sending this page, and since it's postcopy nothing else - * will dirty it, and we must make sure it doesn't get sent again - * even if this queue request was received after the background - * search already sent it. - */ - if (block) { - unsigned long page; - - page = offset >> TARGET_PAGE_BITS; - dirty = test_bit(page, block->bmap); - if (!dirty) { - trace_get_queued_page_not_dirty(block->idstr, (uint64_t)offset, - page); - } else { - trace_get_queued_page(block->idstr, (uint64_t)offset, page); - } - } - - } while (block && !dirty); + block = unqueue_page(rs, &offset); if (!block) { /* diff --git a/migration/trace-events b/migration/trace-events index 171a83a55d..48aa7b10ee 100644 --- a/migration/trace-events +++ b/migration/trace-events @@ -86,8 +86,6 @@ put_qlist_end(const char *field_name, const char *vmsd_name) "%s(%s)" qemu_file_fclose(void) "" # ram.c -get_queued_page(const char *block_name, uint64_t tmp_offset, unsigned long page_abs) "%s/0x%" PRIx64 " page_abs=0x%lx" -get_queued_page_not_dirty(const char *block_name, uint64_t tmp_offset, unsigned long page_abs) "%s/0x%" PRIx64 " page_abs=0x%lx" migration_bitmap_sync_start(void) "" migration_bitmap_sync_end(uint64_t dirty_pages) "dirty_pages %" PRIu64 migration_bitmap_clear_dirty(char *str, uint64_t start, uint64_t size, unsigned long page) "rb %s start 0x%"PRIx64" size 0x%"PRIx64" page 0x%lx" @@ -113,6 +111,7 @@ ram_save_iterate_big_wait(uint64_t milliconds, int iterations) "big wait: %" PRI ram_load_complete(int ret, uint64_t seq_iter) "exit_code %d seq iteration %" PRIu64 ram_write_tracking_ramblock_start(const char *block_id, size_t page_size, void *addr, size_t length) "%s: page_size: %zu addr: %p length: %zu" ram_write_tracking_ramblock_stop(const char *block_id, size_t page_size, void *addr, size_t length) "%s: page_size: %zu addr: %p length: %zu" +unqueue_page(char *block, uint64_t offset, bool dirty) "ramblock '%s' offset 0x%"PRIx64" dirty %d" # multifd.c multifd_new_send_channel_async(uint8_t id) "channel %u" From 476ebf77fe8909ded10046edf26685bc28438162 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Wed, 19 Jan 2022 16:09:20 +0800 Subject: [PATCH 139/460] migration: Move temp page setup and cleanup into separate functions Temp pages will need to grow if we want to have multiple channels for postcopy, because each channel will need its own temp page to cache huge page data. Before doing that, cleanup the related code. No functional change intended. Since at it, touch up the errno handling a little bit on the setup side. Signed-off-by: Peter Xu Reviewed-by: Dr. David Alan Gilbert Reviewed-by: Juan Quintela Signed-off-by: Juan Quintela --- migration/postcopy-ram.c | 82 +++++++++++++++++++++++++--------------- 1 file changed, 51 insertions(+), 31 deletions(-) diff --git a/migration/postcopy-ram.c b/migration/postcopy-ram.c index 2176ed68a5..e662dd05cc 100644 --- a/migration/postcopy-ram.c +++ b/migration/postcopy-ram.c @@ -523,6 +523,19 @@ int postcopy_ram_incoming_init(MigrationIncomingState *mis) return 0; } +static void postcopy_temp_pages_cleanup(MigrationIncomingState *mis) +{ + if (mis->postcopy_tmp_page) { + munmap(mis->postcopy_tmp_page, mis->largest_page_size); + mis->postcopy_tmp_page = NULL; + } + + if (mis->postcopy_tmp_zero_page) { + munmap(mis->postcopy_tmp_zero_page, mis->largest_page_size); + mis->postcopy_tmp_zero_page = NULL; + } +} + /* * At the end of a migration where postcopy_ram_incoming_init was called. */ @@ -564,14 +577,8 @@ int postcopy_ram_incoming_cleanup(MigrationIncomingState *mis) } } - if (mis->postcopy_tmp_page) { - munmap(mis->postcopy_tmp_page, mis->largest_page_size); - mis->postcopy_tmp_page = NULL; - } - if (mis->postcopy_tmp_zero_page) { - munmap(mis->postcopy_tmp_zero_page, mis->largest_page_size); - mis->postcopy_tmp_zero_page = NULL; - } + postcopy_temp_pages_cleanup(mis); + trace_postcopy_ram_incoming_cleanup_blocktime( get_postcopy_total_blocktime()); @@ -1082,6 +1089,40 @@ retry: return NULL; } +static int postcopy_temp_pages_setup(MigrationIncomingState *mis) +{ + int err; + + mis->postcopy_tmp_page = mmap(NULL, mis->largest_page_size, + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (mis->postcopy_tmp_page == MAP_FAILED) { + err = errno; + mis->postcopy_tmp_page = NULL; + error_report("%s: Failed to map postcopy_tmp_page %s", + __func__, strerror(err)); + return -err; + } + + /* + * Map large zero page when kernel can't use UFFDIO_ZEROPAGE for hugepages + */ + mis->postcopy_tmp_zero_page = mmap(NULL, mis->largest_page_size, + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (mis->postcopy_tmp_zero_page == MAP_FAILED) { + err = errno; + mis->postcopy_tmp_zero_page = NULL; + error_report("%s: Failed to map large zero page %s", + __func__, strerror(err)); + return -err; + } + + memset(mis->postcopy_tmp_zero_page, '\0', mis->largest_page_size); + + return 0; +} + int postcopy_ram_incoming_setup(MigrationIncomingState *mis) { /* Open the fd for the kernel to give us userfaults */ @@ -1122,32 +1163,11 @@ int postcopy_ram_incoming_setup(MigrationIncomingState *mis) return -1; } - mis->postcopy_tmp_page = mmap(NULL, mis->largest_page_size, - PROT_READ | PROT_WRITE, MAP_PRIVATE | - MAP_ANONYMOUS, -1, 0); - if (mis->postcopy_tmp_page == MAP_FAILED) { - mis->postcopy_tmp_page = NULL; - error_report("%s: Failed to map postcopy_tmp_page %s", - __func__, strerror(errno)); + if (postcopy_temp_pages_setup(mis)) { + /* Error dumped in the sub-function */ return -1; } - /* - * Map large zero page when kernel can't use UFFDIO_ZEROPAGE for hugepages - */ - mis->postcopy_tmp_zero_page = mmap(NULL, mis->largest_page_size, - PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANONYMOUS, - -1, 0); - if (mis->postcopy_tmp_zero_page == MAP_FAILED) { - int e = errno; - mis->postcopy_tmp_zero_page = NULL; - error_report("%s: Failed to map large zero page %s", - __func__, strerror(e)); - return -e; - } - memset(mis->postcopy_tmp_zero_page, '\0', mis->largest_page_size); - trace_postcopy_ram_enable_notify(); return 0; From 4537d62dcece45183632298272abfb4859418cc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Mon, 24 Jan 2022 18:41:30 +0100 Subject: [PATCH 140/460] target/ppc: Remove support for the PowerPC 602 CPU MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 602 was derived from the PowerPC 603, for the gaming market it seems. It was hardly used and no firmware supporting the CPU could be found. Drop support. Signed-off-by: Cédric Le Goater --- target/ppc/cpu-models.c | 2 - target/ppc/cpu-models.h | 1 - target/ppc/cpu.h | 8 +- target/ppc/cpu_init.c | 147 ----------------------------------- target/ppc/excp_helper.c | 1 - target/ppc/helper.h | 1 - target/ppc/int_helper.c | 21 ----- target/ppc/mfrom_table.c.inc | 78 ------------------- target/ppc/mfrom_table_gen.c | 34 -------- target/ppc/translate.c | 30 ------- 10 files changed, 1 insertion(+), 322 deletions(-) delete mode 100644 target/ppc/mfrom_table.c.inc delete mode 100644 target/ppc/mfrom_table_gen.c diff --git a/target/ppc/cpu-models.c b/target/ppc/cpu-models.c index 764afe5a2a..a2c720cc4d 100644 --- a/target/ppc/cpu-models.c +++ b/target/ppc/cpu-models.c @@ -428,8 +428,6 @@ "PowerPC 601v1") POWERPC_DEF("601_v2", CPU_POWERPC_601_v2, 601v, "PowerPC 601v2") - POWERPC_DEF("602", CPU_POWERPC_602, 602, - "PowerPC 602") POWERPC_DEF("603", CPU_POWERPC_603, 603, "PowerPC 603") POWERPC_DEF("603e_v1.1", CPU_POWERPC_603E_v11, 603E, diff --git a/target/ppc/cpu-models.h b/target/ppc/cpu-models.h index bf1dc7e5ca..612978a3fb 100644 --- a/target/ppc/cpu-models.h +++ b/target/ppc/cpu-models.h @@ -208,7 +208,6 @@ enum { CPU_POWERPC_601_v0 = 0x00010001, CPU_POWERPC_601_v1 = 0x00010001, CPU_POWERPC_601_v2 = 0x00010002, - CPU_POWERPC_602 = 0x00050100, CPU_POWERPC_603 = 0x00030100, CPU_POWERPC_603E_v11 = 0x00060101, CPU_POWERPC_603E_v12 = 0x00060102, diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index 66e13075c3..dcd83b503c 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -321,9 +321,7 @@ typedef enum { #define MSR_UCLE 26 /* User-mode cache lock enable for BookE */ #define MSR_VR 25 /* altivec available x hflags */ #define MSR_SPE 25 /* SPE enable for BookE x hflags */ -#define MSR_AP 23 /* Access privilege state on 602 hflags */ #define MSR_VSX 23 /* Vector Scalar Extension (ISA 2.06 and later) x hflags */ -#define MSR_SA 22 /* Supervisor access mode on 602 hflags */ #define MSR_S 22 /* Secure state */ #define MSR_KEY 19 /* key bit on 603e */ #define MSR_POW 18 /* Power management */ @@ -477,9 +475,7 @@ typedef enum { #define msr_ucle ((env->msr >> MSR_UCLE) & 1) #define msr_vr ((env->msr >> MSR_VR) & 1) #define msr_spe ((env->msr >> MSR_SPE) & 1) -#define msr_ap ((env->msr >> MSR_AP) & 1) #define msr_vsx ((env->msr >> MSR_VSX) & 1) -#define msr_sa ((env->msr >> MSR_SA) & 1) #define msr_key ((env->msr >> MSR_KEY) & 1) #define msr_pow ((env->msr >> MSR_POW) & 1) #define msr_tgpr ((env->msr >> MSR_TGPR) & 1) @@ -2142,8 +2138,6 @@ enum { PPC_MFTB = 0x0000000000000200ULL, /* Fixed-point unit extensions */ - /* PowerPC 602 specific */ - PPC_602_SPEC = 0x0000000000000400ULL, /* isel instruction */ PPC_ISEL = 0x0000000000000800ULL, /* popcntb instruction */ @@ -2245,7 +2239,7 @@ enum { #define PPC_TCG_INSNS (PPC_INSNS_BASE | PPC_POWER | PPC_POWER2 \ | PPC_POWER_RTC | PPC_POWER_BR | PPC_64B \ | PPC_64BX | PPC_64H | PPC_WAIT | PPC_MFTB \ - | PPC_602_SPEC | PPC_ISEL | PPC_POPCNTB \ + | PPC_ISEL | PPC_POPCNTB \ | PPC_STRING | PPC_FLOAT | PPC_FLOAT_EXT \ | PPC_FLOAT_FSQRT | PPC_FLOAT_FRES \ | PPC_FLOAT_FRSQRTE | PPC_FLOAT_FRSQRTES \ diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index 23a13036b2..bf60529d37 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -749,54 +749,6 @@ static void register_G2_sprs(CPUPPCState *env) 0x00000000); } -/* SPR specific to PowerPC 602 implementation */ -static void register_602_sprs(CPUPPCState *env) -{ - /* ESA registers */ - /* XXX : not implemented */ - spr_register(env, SPR_SER, "SER", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, - 0x00000000); - /* XXX : not implemented */ - spr_register(env, SPR_SEBR, "SEBR", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, - 0x00000000); - /* XXX : not implemented */ - spr_register(env, SPR_ESASRR, "ESASRR", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, - 0x00000000); - /* Floating point status */ - /* XXX : not implemented */ - spr_register(env, SPR_SP, "SP", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, - 0x00000000); - /* XXX : not implemented */ - spr_register(env, SPR_LT, "LT", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, - 0x00000000); - /* Watchdog timer */ - /* XXX : not implemented */ - spr_register(env, SPR_TCR, "TCR", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, - 0x00000000); - /* Interrupt base */ - spr_register(env, SPR_IBR, "IBR", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, - 0x00000000); - /* XXX : not implemented */ - spr_register(env, SPR_IABR, "IABR", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, - 0x00000000); -} - /* SPR specific to PowerPC 601 implementation */ static void register_601_sprs(CPUPPCState *env) { @@ -2128,33 +2080,6 @@ static void init_excp_601(CPUPPCState *env) #endif } -static void init_excp_602(CPUPPCState *env) -{ -#if !defined(CONFIG_USER_ONLY) - /* XXX: exception prefix has a special behavior on 602 */ - env->excp_vectors[POWERPC_EXCP_RESET] = 0x00000100; - env->excp_vectors[POWERPC_EXCP_MCHECK] = 0x00000200; - env->excp_vectors[POWERPC_EXCP_DSI] = 0x00000300; - env->excp_vectors[POWERPC_EXCP_ISI] = 0x00000400; - env->excp_vectors[POWERPC_EXCP_EXTERNAL] = 0x00000500; - env->excp_vectors[POWERPC_EXCP_ALIGN] = 0x00000600; - env->excp_vectors[POWERPC_EXCP_PROGRAM] = 0x00000700; - env->excp_vectors[POWERPC_EXCP_FPU] = 0x00000800; - env->excp_vectors[POWERPC_EXCP_DECR] = 0x00000900; - env->excp_vectors[POWERPC_EXCP_SYSCALL] = 0x00000C00; - env->excp_vectors[POWERPC_EXCP_TRACE] = 0x00000D00; - env->excp_vectors[POWERPC_EXCP_IFTLB] = 0x00001000; - env->excp_vectors[POWERPC_EXCP_DLTLB] = 0x00001100; - env->excp_vectors[POWERPC_EXCP_DSTLB] = 0x00001200; - env->excp_vectors[POWERPC_EXCP_IABR] = 0x00001300; - env->excp_vectors[POWERPC_EXCP_SMI] = 0x00001400; - env->excp_vectors[POWERPC_EXCP_WDT] = 0x00001500; - env->excp_vectors[POWERPC_EXCP_EMUL] = 0x00001600; - /* Hardware reset vector */ - env->hreset_vector = 0x00000100UL; -#endif -} - static void init_excp_603(CPUPPCState *env) { #if !defined(CONFIG_USER_ONLY) @@ -4081,76 +4006,6 @@ POWERPC_FAMILY(601v)(ObjectClass *oc, void *data) pcc->flags = POWERPC_FLAG_SE | POWERPC_FLAG_RTC_CLK | POWERPC_FLAG_HID0_LE; } -static void init_proc_602(CPUPPCState *env) -{ - register_ne_601_sprs(env); - register_sdr1_sprs(env); - register_602_sprs(env); - /* Time base */ - register_tbl(env); - /* hardware implementation registers */ - /* XXX : not implemented */ - spr_register(env, SPR_HID0, "HID0", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, - 0x00000000); - /* XXX : not implemented */ - spr_register(env, SPR_HID1, "HID1", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, - 0x00000000); - /* Memory management */ - register_low_BATs(env); - register_6xx_7xx_soft_tlb(env, 64, 2); - init_excp_602(env); - env->dcache_line_size = 32; - env->icache_line_size = 32; - /* Allocate hardware IRQ controller */ - ppc6xx_irq_init(env_archcpu(env)); -} - -POWERPC_FAMILY(602)(ObjectClass *oc, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(oc); - PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); - - dc->desc = "PowerPC 602"; - pcc->init_proc = init_proc_602; - pcc->check_pow = check_pow_hid0; - pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB | - PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES | - PPC_FLOAT_FRSQRTE | PPC_FLOAT_STFIWX | - PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | - PPC_MEM_SYNC | PPC_MEM_EIEIO | - PPC_MEM_TLBIE | PPC_6xx_TLB | PPC_MEM_TLBSYNC | - PPC_SEGMENT | PPC_602_SPEC; - pcc->msr_mask = (1ull << MSR_VSX) | - (1ull << MSR_SA) | - (1ull << MSR_POW) | - (1ull << MSR_TGPR) | - (1ull << MSR_ILE) | - (1ull << MSR_EE) | - (1ull << MSR_PR) | - (1ull << MSR_FP) | - (1ull << MSR_ME) | - (1ull << MSR_FE0) | - (1ull << MSR_SE) | - (1ull << MSR_DE) | - (1ull << MSR_FE1) | - (1ull << MSR_EP) | - (1ull << MSR_IR) | - (1ull << MSR_DR) | - (1ull << MSR_RI) | - (1ull << MSR_LE); - /* XXX: 602 MMU is quite specific. Should add a special case */ - pcc->mmu_model = POWERPC_MMU_SOFT_6xx; - pcc->excp_model = POWERPC_EXCP_602; - pcc->bus_model = PPC_FLAGS_INPUT_6xx; - pcc->bfd_mach = bfd_mach_ppc_602; - pcc->flags = POWERPC_FLAG_TGPR | POWERPC_FLAG_SE | - POWERPC_FLAG_BE | POWERPC_FLAG_BUS_CLK; -} - static void init_proc_603(CPUPPCState *env) { register_ne_601_sprs(env); @@ -8271,8 +8126,6 @@ static void ppc_cpu_reset(DeviceState *dev) msr = (target_ulong)0; msr |= (target_ulong)MSR_HVB; - msr |= (target_ulong)0 << MSR_AP; /* TO BE CHECKED */ - msr |= (target_ulong)0 << MSR_SA; /* TO BE CHECKED */ msr |= (target_ulong)1 << MSR_EP; #if defined(DO_SINGLE_STEP) && 0 /* Single step trace mode */ diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index 4e6bb87b70..c107953dec 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -1422,7 +1422,6 @@ static inline void powerpc_excp_legacy(PowerPCCPU *cpu, int excp) case POWERPC_EXCP_DLTLB: /* Data load TLB miss */ case POWERPC_EXCP_DSTLB: /* Data store TLB miss */ switch (excp_model) { - case POWERPC_EXCP_602: case POWERPC_EXCP_603: case POWERPC_EXCP_G2: /* Swap temporary saved registers with GPRs */ diff --git a/target/ppc/helper.h b/target/ppc/helper.h index bdbbd5e1d9..f2e5060910 100644 --- a/target/ppc/helper.h +++ b/target/ppc/helper.h @@ -646,7 +646,6 @@ DEF_HELPER_FLAGS_2(slbieg, TCG_CALL_NO_RWG, void, env, tl) DEF_HELPER_FLAGS_2(load_sr, TCG_CALL_NO_RWG, tl, env, tl) DEF_HELPER_FLAGS_3(store_sr, TCG_CALL_NO_RWG, void, env, tl, tl) -DEF_HELPER_FLAGS_1(602_mfrom, TCG_CALL_NO_RWG_SE, tl, tl) DEF_HELPER_1(msgsnd, void, tl) DEF_HELPER_2(msgclr, void, env, tl) DEF_HELPER_1(book3s_msgsnd, void, tl) diff --git a/target/ppc/int_helper.c b/target/ppc/int_helper.c index 9bc327bcba..d7765fd3e3 100644 --- a/target/ppc/int_helper.c +++ b/target/ppc/int_helper.c @@ -488,27 +488,6 @@ target_ulong helper_divso(CPUPPCState *env, target_ulong arg1, } } -/*****************************************************************************/ -/* 602 specific instructions */ -/* mfrom is the most crazy instruction ever seen, imho ! */ -/* Real implementation uses a ROM table. Do the same */ -/* - * Extremely decomposed: - * -arg / 256 - * return 256 * log10(10 + 1.0) + 0.5 - */ -#if !defined(CONFIG_USER_ONLY) -target_ulong helper_602_mfrom(target_ulong arg) -{ - if (likely(arg < 602)) { -#include "mfrom_table.c.inc" - return mfrom_ROM_table[arg]; - } else { - return 0; - } -} -#endif - /*****************************************************************************/ /* Altivec extension helpers */ #if defined(HOST_WORDS_BIGENDIAN) diff --git a/target/ppc/mfrom_table.c.inc b/target/ppc/mfrom_table.c.inc deleted file mode 100644 index 1653b974a4..0000000000 --- a/target/ppc/mfrom_table.c.inc +++ /dev/null @@ -1,78 +0,0 @@ -static const uint8_t mfrom_ROM_table[602] = { - 77, 77, 76, 76, 75, 75, 74, 74, - 73, 73, 72, 72, 71, 71, 70, 70, - 69, 69, 68, 68, 68, 67, 67, 66, - 66, 65, 65, 64, 64, 64, 63, 63, - 62, 62, 61, 61, 61, 60, 60, 59, - 59, 58, 58, 58, 57, 57, 56, 56, - 56, 55, 55, 54, 54, 54, 53, 53, - 53, 52, 52, 51, 51, 51, 50, 50, - 50, 49, 49, 49, 48, 48, 47, 47, - 47, 46, 46, 46, 45, 45, 45, 44, - 44, 44, 43, 43, 43, 42, 42, 42, - 42, 41, 41, 41, 40, 40, 40, 39, - 39, 39, 39, 38, 38, 38, 37, 37, - 37, 37, 36, 36, 36, 35, 35, 35, - 35, 34, 34, 34, 34, 33, 33, 33, - 33, 32, 32, 32, 32, 31, 31, 31, - 31, 30, 30, 30, 30, 29, 29, 29, - 29, 28, 28, 28, 28, 28, 27, 27, - 27, 27, 26, 26, 26, 26, 26, 25, - 25, 25, 25, 25, 24, 24, 24, 24, - 24, 23, 23, 23, 23, 23, 23, 22, - 22, 22, 22, 22, 21, 21, 21, 21, - 21, 21, 20, 20, 20, 20, 20, 20, - 19, 19, 19, 19, 19, 19, 19, 18, - 18, 18, 18, 18, 18, 17, 17, 17, - 17, 17, 17, 17, 16, 16, 16, 16, - 16, 16, 16, 16, 15, 15, 15, 15, - 15, 15, 15, 15, 14, 14, 14, 14, - 14, 14, 14, 14, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 12, 12, 12, - 12, 12, 12, 12, 12, 12, 12, 11, - 11, 11, 11, 11, 11, 11, 11, 11, - 11, 11, 10, 10, 10, 10, 10, 10, - 10, 10, 10, 10, 10, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, - 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, - 7, 6, 6, 6, 6, 6, 6, 6, - 6, 6, 6, 6, 6, 6, 6, 6, - 6, 6, 6, 6, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 3, - 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 1, 0, -}; diff --git a/target/ppc/mfrom_table_gen.c b/target/ppc/mfrom_table_gen.c deleted file mode 100644 index f96c4268ba..0000000000 --- a/target/ppc/mfrom_table_gen.c +++ /dev/null @@ -1,34 +0,0 @@ -#define _GNU_SOURCE -#include "qemu/osdep.h" -#include - -int main(void) -{ - double d; - uint8_t n; - int i; - - printf("static const uint8_t mfrom_ROM_table[602] =\n{\n "); - for (i = 0; i < 602; i++) { - /* - * Extremely decomposed: - * -T0 / 256 - * T0 = 256 * log10(10 + 1.0) + 0.5 - */ - d = -i; - d /= 256.0; - d = exp10(d); - d += 1.0; - d = log10(d); - d *= 256; - d += 0.5; - n = d; - printf("%3d, ", n); - if ((i & 7) == 7) { - printf("\n "); - } - } - printf("\n};\n"); - - return 0; -} diff --git a/target/ppc/translate.c b/target/ppc/translate.c index d61c6f0e8f..c2f436f8d3 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -6272,33 +6272,6 @@ static void gen_srq(DisasContext *ctx) } } -/* PowerPC 602 specific instructions */ - -/* dsa */ -static void gen_dsa(DisasContext *ctx) -{ - /* XXX: TODO */ - gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); -} - -/* esa */ -static void gen_esa(DisasContext *ctx) -{ - /* XXX: TODO */ - gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); -} - -/* mfrom */ -static void gen_mfrom(DisasContext *ctx) -{ -#if defined(CONFIG_USER_ONLY) - GEN_PRIV; -#else - CHK_SV; - gen_helper_602_mfrom(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)]); -#endif /* defined(CONFIG_USER_ONLY) */ -} - /* 602 - 603 - G2 TLB management */ /* tlbld */ @@ -7779,9 +7752,6 @@ GEN_HANDLER(sriq, 0x1F, 0x18, 0x15, 0x00000000, PPC_POWER_BR), GEN_HANDLER(srliq, 0x1F, 0x18, 0x17, 0x00000000, PPC_POWER_BR), GEN_HANDLER(srlq, 0x1F, 0x18, 0x16, 0x00000000, PPC_POWER_BR), GEN_HANDLER(srq, 0x1F, 0x18, 0x14, 0x00000000, PPC_POWER_BR), -GEN_HANDLER(dsa, 0x1F, 0x14, 0x13, 0x03FFF801, PPC_602_SPEC), -GEN_HANDLER(esa, 0x1F, 0x14, 0x12, 0x03FFF801, PPC_602_SPEC), -GEN_HANDLER(mfrom, 0x1F, 0x09, 0x08, 0x03E0F801, PPC_602_SPEC), GEN_HANDLER2(tlbld_6xx, "tlbld", 0x1F, 0x12, 0x1E, 0x03FF0001, PPC_6xx_TLB), GEN_HANDLER2(tlbli_6xx, "tlbli", 0x1F, 0x12, 0x1F, 0x03FF0001, PPC_6xx_TLB), GEN_HANDLER(clf, 0x1F, 0x16, 0x03, 0x03E00000, PPC_POWER), From 523f5a9971c0766aefa2cfe42c4e7c3b54a21da6 Mon Sep 17 00:00:00 2001 From: Nir Soffer Date: Tue, 11 Jan 2022 21:43:13 +0200 Subject: [PATCH 141/460] nbd/server.c: Remove unused field MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit NBDRequestData struct has unused QSIMPLEQ_ENTRY field. It seems that this field exists since the first git commit and was never used. Signed-off-by: Nir Soffer Message-Id: <20220111194313.581486-1-nsoffer@redhat.com> Reviewed-by: Philippe Mathieu-Daudé Fixes: d9a73806 ("qemu-nbd: introduce NBDRequest", v1.1) Signed-off-by: Eric Blake --- nbd/server.c | 1 - 1 file changed, 1 deletion(-) diff --git a/nbd/server.c b/nbd/server.c index 4630dd7322..9fb2f26402 100644 --- a/nbd/server.c +++ b/nbd/server.c @@ -77,7 +77,6 @@ static int system_errno_to_nbd_errno(int err) typedef struct NBDRequestData NBDRequestData; struct NBDRequestData { - QSIMPLEQ_ENTRY(NBDRequestData) entry; NBDClient *client; uint8_t *data; bool complete; From 3a8fa0edd18f76e222d983a31e486e76a51348a7 Mon Sep 17 00:00:00 2001 From: Philippe Mathieu-Daude Date: Wed, 19 Jan 2022 13:14:39 +0100 Subject: [PATCH 142/460] qapi/block: Cosmetic change in BlockExportType schema MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix long line introduced in commit bb01ea73110 ("qapi/block: Restrict vhost-user-blk to CONFIG_VHOST_USER_BLK_SERVER"). Suggested-by: Markus Armbruster Acked-by: Markus Armbruster Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20220119121439.214821-1-f4bug@amsat.org> Reviewed-by: Eric Blake Signed-off-by: Eric Blake --- qapi/block-export.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/qapi/block-export.json b/qapi/block-export.json index f9ce79a974..f183522d0d 100644 --- a/qapi/block-export.json +++ b/qapi/block-export.json @@ -278,7 +278,8 @@ ## { 'enum': 'BlockExportType', 'data': [ 'nbd', - { 'name': 'vhost-user-blk', 'if': 'CONFIG_VHOST_USER_BLK_SERVER' }, + { 'name': 'vhost-user-blk', + 'if': 'CONFIG_VHOST_USER_BLK_SERVER' }, { 'name': 'fuse', 'if': 'CONFIG_FUSE' } ] } ## From 4550c661bfa3fd4aba2d5dec7b732546124d8958 Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Mon, 24 Jan 2022 01:33:02 -0700 Subject: [PATCH 143/460] bsd-user: Complete FreeBSD siginfo Fill in the missing FreeBSD siginfo fields, and add some comments. Signed-off-by: Warner Losh Reviewed-by: Richard Henderson --- bsd-user/freebsd/target_os_siginfo.h | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/bsd-user/freebsd/target_os_siginfo.h b/bsd-user/freebsd/target_os_siginfo.h index 84944faa4d..d50a3034a8 100644 --- a/bsd-user/freebsd/target_os_siginfo.h +++ b/bsd-user/freebsd/target_os_siginfo.h @@ -71,11 +71,24 @@ typedef struct target_siginfo { int32_t _mqd; } _mesgp; - /* SIGPOLL */ + /* SIGPOLL -- Not really genreated in FreeBSD ??? */ struct { int _band; /* POLL_IN, POLL_OUT, POLL_MSG */ } _poll; + struct { + int _mqd; + } _mesgq; + + struct { + /* + * Syscall number for signals delivered as a result of system calls + * denied by Capsicum. + */ + int _syscall; + } _capsicum; + + /* Spare for future growth */ struct { abi_long __spare1__; int32_t __spare2_[7]; From 2373a62ae9600aef57923fdba0518e916cc8d28c Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Wed, 19 Jan 2022 11:46:05 -0700 Subject: [PATCH 144/460] bsd-user: Create setup_sigframe_arch to setup sigframe context Define setup_sigframe_arch whose job it is to setup the mcontext for the sigframe. Implement for x86 to just call mcontext. Signed-off-by: Warner Losh Reviewed-by: Richard Henderson --- bsd-user/freebsd/target_os_signal.h | 3 +++ bsd-user/i386/signal.c | 13 +++++++++++++ bsd-user/x86_64/signal.c | 13 +++++++++++++ 3 files changed, 29 insertions(+) diff --git a/bsd-user/freebsd/target_os_signal.h b/bsd-user/freebsd/target_os_signal.h index 3ed454e086..43700d08f7 100644 --- a/bsd-user/freebsd/target_os_signal.h +++ b/bsd-user/freebsd/target_os_signal.h @@ -4,6 +4,9 @@ #include "target_os_siginfo.h" #include "target_arch_signal.h" +abi_long setup_sigframe_arch(CPUArchState *env, abi_ulong frame_addr, + struct target_sigframe *frame, int flags); + /* Compare to sys/signal.h */ #define TARGET_SIGHUP 1 /* hangup */ #define TARGET_SIGINT 2 /* interrupt */ diff --git a/bsd-user/i386/signal.c b/bsd-user/i386/signal.c index 2939d32400..5dd975ce56 100644 --- a/bsd-user/i386/signal.c +++ b/bsd-user/i386/signal.c @@ -32,6 +32,19 @@ abi_long set_sigtramp_args(CPUX86State *env, int sig, return 0; } +/* + * Compare to i386/i386/exec_machdep.c sendsig() + * Assumes that the memory is locked if frame points to user memory. + */ +abi_long setup_sigframe_arch(CPUX86State *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; +} + /* Compare to i386/i386/machdep.c get_mcontext() */ abi_long get_mcontext(CPUX86State *regs, target_mcontext_t *mcp, int flags) { diff --git a/bsd-user/x86_64/signal.c b/bsd-user/x86_64/signal.c index 8885152a7d..c3875bc4c6 100644 --- a/bsd-user/x86_64/signal.c +++ b/bsd-user/x86_64/signal.c @@ -30,6 +30,19 @@ abi_long set_sigtramp_args(CPUX86State *regs, return 0; } +/* + * Compare to amd64/amd64/exec_machdep.c sendsig() + * Assumes that the memory is locked if frame points to user memory. + */ +abi_long setup_sigframe_arch(CPUX86State *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; +} + /* Compare to amd64/amd64/machdep.c get_mcontext() */ abi_long get_mcontext(CPUX86State *regs, target_mcontext_t *mcp, int flags) From 224474622e61f24c4b991fca03e32113eaac91cb Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Wed, 19 Jan 2022 11:49:05 -0700 Subject: [PATCH 145/460] bsd-user/arm/signal.c: Implement setup_sigframe_arch for arm Fix the broken context setting for arm. FreeBSD's get_mcontext does not fill in the vfp info. It's filled in in sigframe(). This corresponds to the new setup_sigframe_arch which fills in mcontext, then adjusts it to point to the vfp context in the sigframe and fills in that context as well. Add pointer to where this code is done. Signed-off-by: Warner Losh Reviewed-by: Richard Henderson --- bsd-user/arm/signal.c | 50 ++++++++++++++++++++++++++++--------------- 1 file changed, 33 insertions(+), 17 deletions(-) diff --git a/bsd-user/arm/signal.c b/bsd-user/arm/signal.c index 1478f008d1..9026343b47 100644 --- a/bsd-user/arm/signal.c +++ b/bsd-user/arm/signal.c @@ -59,19 +59,31 @@ abi_long set_sigtramp_args(CPUARMState *env, int sig, return 0; } +static abi_long get_vfpcontext(CPUARMState *env, abi_ulong frame_addr, + struct target_sigframe *frame) +{ + /* see sendsig and get_vfpcontext in sys/arm/arm/exec_machdep.c */ + target_mcontext_vfp_t *vfp = &frame->sf_vfp; + target_mcontext_t *mcp = &frame->sf_uc.uc_mcontext; + + /* Assumes that mcp and vfp are locked */ + for (int i = 0; i < 32; i++) { + vfp->mcv_reg[i] = tswap64(*aa32_vfp_dreg(env, i)); + } + vfp->mcv_fpscr = tswap32(vfp_get_fpscr(env)); + mcp->mc_vfp_size = tswap32(sizeof(*vfp)); + mcp->mc_vfp_ptr = tswap32(frame_addr + ((uintptr_t)vfp - (uintptr_t)frame)); + return 0; +} + /* - * Compare to arm/arm/machdep.c get_mcontext() + * Compare to arm/arm/exec_machdep.c get_mcontext() * Assumes that the memory is locked if mcp points to user memory. */ abi_long get_mcontext(CPUARMState *env, target_mcontext_t *mcp, int flags) { - int err = 0; uint32_t *gr = mcp->__gregs; - if (mcp->mc_vfp_size != 0 && mcp->mc_vfp_size != sizeof(target_mcontext_vfp_t)) { - return -TARGET_EINVAL; - } - gr[TARGET_REG_CPSR] = tswap32(cpsr_read(env)); if (flags & TARGET_MC_GET_CLEAR_RET) { gr[TARGET_REG_R0] = 0; @@ -97,17 +109,21 @@ abi_long get_mcontext(CPUARMState *env, target_mcontext_t *mcp, int flags) gr[TARGET_REG_LR] = tswap32(env->regs[14]); gr[TARGET_REG_PC] = tswap32(env->regs[15]); - if (mcp->mc_vfp_size != 0 && mcp->mc_vfp_ptr != 0) { - /* see get_vfpcontext in sys/arm/arm/exec_machdep.c */ - target_mcontext_vfp_t *vfp; - vfp = lock_user(VERIFY_WRITE, mcp->mc_vfp_ptr, sizeof(*vfp), 0); - for (int i = 0; i < 32; i++) { - vfp->mcv_reg[i] = tswap64(*aa32_vfp_dreg(env, i)); - } - vfp->mcv_fpscr = tswap32(vfp_get_fpscr(env)); - unlock_user(vfp, mcp->mc_vfp_ptr, sizeof(*vfp)); - } - return err; + return 0; +} + +/* + * Compare to arm/arm/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); + get_vfpcontext(env, frame_addr, frame); + return 0; } /* Compare to arm/arm/exec_machdep.c set_mcontext() */ From 7f96d0a93c9f252fc65b0ad49121a62889ec560e Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Mon, 24 Jan 2022 01:29:53 -0700 Subject: [PATCH 146/460] bsd-user/arm/signal.c: get_mcontext should zero vfp data FreeBSD's get_mcontext doesn't return any vfp data. Instead, it zeros out the vfp feilds (and all the spare fields). Impelement this behavior. We're still missing the sysarch(ARM_GET_VFPCONTEXT) syscall, though. Signed-off-by: Warner Losh Reviewed-by: Richard Henderson --- bsd-user/arm/signal.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/bsd-user/arm/signal.c b/bsd-user/arm/signal.c index 9026343b47..2b1dd745d1 100644 --- a/bsd-user/arm/signal.c +++ b/bsd-user/arm/signal.c @@ -109,6 +109,15 @@ abi_long get_mcontext(CPUARMState *env, target_mcontext_t *mcp, int flags) gr[TARGET_REG_LR] = tswap32(env->regs[14]); gr[TARGET_REG_PC] = tswap32(env->regs[15]); + /* + * FreeBSD's get_mcontext doesn't save VFP info, but sets the pointer and + * size to zero. Applications that need the VFP state use + * sysarch(ARM_GET_VFPSTATE) and are expected to adjust mcontext after that. + */ + mcp->mc_vfp_size = 0; + mcp->mc_vfp_ptr = 0; + memset(&mcp->mc_spare, 0, sizeof(mcp->mc_spare)); + return 0; } From b46d4ad7d135d43eb6141e298b3fed9236f4caeb Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Sun, 16 Jan 2022 16:14:18 -0700 Subject: [PATCH 147/460] bsd-user: Remove vestiges of signal queueing code bsd-user was copied from linux-user at a time when it queued signals. Remove those vestiges of thse code. Retain the init function, even though it's now empty since other stuff will likely be added there. Make it static since it's not called from outside of main.c Signed-off-by: Warner Losh Reviewed-by: Richard Henderson --- bsd-user/main.c | 9 +-------- bsd-user/qemu.h | 13 +------------ 2 files changed, 2 insertions(+), 20 deletions(-) diff --git a/bsd-user/main.c b/bsd-user/main.c index cb5ea40236..29cf4e1569 100644 --- a/bsd-user/main.c +++ b/bsd-user/main.c @@ -215,15 +215,8 @@ void qemu_cpu_kick(CPUState *cpu) } /* Assumes contents are already zeroed. */ -void init_task_state(TaskState *ts) +static void init_task_state(TaskState *ts) { - int i; - - ts->first_free = ts->sigqueue_table; - for (i = 0; i < MAX_SIGQUEUE_SIZE - 1; i++) { - ts->sigqueue_table[i].next = &ts->sigqueue_table[i + 1]; - } - ts->sigqueue_table[i].next = NULL; } void gemu_log(const char *fmt, ...) diff --git a/bsd-user/qemu.h b/bsd-user/qemu.h index 1b3b974afe..4dd209e402 100644 --- a/bsd-user/qemu.h +++ b/bsd-user/qemu.h @@ -70,17 +70,9 @@ struct image_info { uint32_t elf_flags; }; -#define MAX_SIGQUEUE_SIZE 1024 - -struct qemu_sigqueue { - struct qemu_sigqueue *next; - target_siginfo_t info; -}; - struct emulated_sigtable { int pending; /* true if signal is pending */ - struct qemu_sigqueue *first; - struct qemu_sigqueue info; /* Put first signal info here */ + target_siginfo_t info; }; /* @@ -94,14 +86,11 @@ typedef struct TaskState { struct image_info *info; struct emulated_sigtable sigtab[TARGET_NSIG]; - struct qemu_sigqueue sigqueue_table[MAX_SIGQUEUE_SIZE]; /* siginfo queue */ - struct qemu_sigqueue *first_free; /* first free siginfo queue entry */ int signal_pending; /* non zero if a signal may be pending */ uint8_t stack[]; } __attribute__((aligned(16))) TaskState; -void init_task_state(TaskState *ts); void stop_all_tasks(void); extern const char *qemu_uname_release; From 4804722593bd1735ce810e380247788200bcb961 Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Sun, 16 Jan 2022 16:33:21 -0700 Subject: [PATCH 148/460] bsd-user: Bring in docs from linux-user for signal_pending This is currently unused, so no code adjustments are needed. Signed-off-by: Warner Losh Reviewed-by: Richard Henderson --- bsd-user/qemu.h | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/bsd-user/qemu.h b/bsd-user/qemu.h index 4dd209e402..671b26f00c 100644 --- a/bsd-user/qemu.h +++ b/bsd-user/qemu.h @@ -86,7 +86,14 @@ typedef struct TaskState { struct image_info *info; struct emulated_sigtable sigtab[TARGET_NSIG]; - int signal_pending; /* non zero if a signal may be pending */ + /* + * Nonzero if process_pending_signals() needs to do something (either + * handle a pending signal or unblock signals). + * This flag is written from a signal handler so should be accessed via + * the qatomic_read() and qatomic_set() functions. (It is not accessed + * from multiple threads.) + */ + int signal_pending; uint8_t stack[]; } __attribute__((aligned(16))) TaskState; From c0d2691ccce7828ade341a263df1d51ce1dfe9ff Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Sat, 8 Jan 2022 15:41:10 -0700 Subject: [PATCH 149/460] bsd-user/arm/target_arch_cpu.h: Move EXCP_ATOMIC to match linux-user Move the EXCP_ATOMIC case to match linux-user/arm/cpu_loop.c:cpu_loop ordering. Signed-off-by: Warner Losh Reviewed-by: Peter Maydell Reviewed-by: Richard Henderson --- bsd-user/arm/target_arch_cpu.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bsd-user/arm/target_arch_cpu.h b/bsd-user/arm/target_arch_cpu.h index c675419c30..c526fc7350 100644 --- a/bsd-user/arm/target_arch_cpu.h +++ b/bsd-user/arm/target_arch_cpu.h @@ -180,12 +180,12 @@ static inline void target_cpu_loop(CPUARMState *env) queue_signal(env, info.si_signo, &info); } 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; + case EXCP_ATOMIC: + cpu_exec_step_atomic(cs); + break; default: fprintf(stderr, "qemu: unhandled CPU exception 0x%x - aborting\n", trapnr); From 0ef599897345e0a43b3741a9990866c92a33d6e9 Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Sat, 8 Jan 2022 15:58:34 -0700 Subject: [PATCH 150/460] bsd-user/signal.c: implement force_sig_fault Start to implement the force_sig_fault code. This currently just calls queue_signal(). The bsd-user fork version of that will handle this the synchronous nature of this call. Add signal-common.h to hold signal helper functions like force_sig_fault. Signed-off-by: Stacey Son Signed-off-by: Kyle Evans Signed-off-by: Warner Losh Reviewed-by: Peter Maydell Reviewed-by: Richard Henderson --- bsd-user/signal-common.h | 14 ++++++++++++++ bsd-user/signal.c | 18 ++++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 bsd-user/signal-common.h diff --git a/bsd-user/signal-common.h b/bsd-user/signal-common.h new file mode 100644 index 0000000000..6207417d39 --- /dev/null +++ b/bsd-user/signal-common.h @@ -0,0 +1,14 @@ +/* + * Emulation of BSD signals + * + * Copyright (c) 2013 Stacey Son + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef SIGNAL_COMMON_H +#define SIGNAL_COMMON_H + +void force_sig_fault(int sig, int code, abi_ulong addr); + +#endif diff --git a/bsd-user/signal.c b/bsd-user/signal.c index 05b277c642..1206d0d728 100644 --- a/bsd-user/signal.c +++ b/bsd-user/signal.c @@ -19,6 +19,7 @@ #include "qemu/osdep.h" #include "qemu.h" +#include "signal-common.h" /* * Stubbed out routines until we merge signal support from bsd-user @@ -34,6 +35,23 @@ void queue_signal(CPUArchState *env, int sig, target_siginfo_t *info) qemu_log_mask(LOG_UNIMP, "No signal queueing, dropping signal %d\n", sig); } +/* + * Force a synchronously taken QEMU_SI_FAULT signal. For QEMU the + * 'force' part is handled in process_pending_signals(). + */ +void force_sig_fault(int sig, int code, abi_ulong addr) +{ + CPUState *cpu = thread_cpu; + CPUArchState *env = cpu->env_ptr; + target_siginfo_t info = {}; + + info.si_signo = sig; + info.si_errno = 0; + info.si_code = code; + info.si_addr = addr; + queue_signal(env, sig, &info); +} + void signal_init(void) { } From 2bd010c4bfdaecee33f3ba4a785ccaaf84df25c1 Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Mon, 24 Jan 2022 16:11:46 -0700 Subject: [PATCH 151/460] bsd-user/signal-common.h: Move signal functions prototypes to here Signed-off-by: Warner Losh Reviewed-by: Richard Henderson --- bsd-user/arm/target_arch_cpu.h | 1 + bsd-user/i386/target_arch_cpu.h | 1 + bsd-user/qemu.h | 8 -------- bsd-user/signal-common.h | 6 ++++++ bsd-user/x86_64/target_arch_cpu.h | 1 + 5 files changed, 9 insertions(+), 8 deletions(-) diff --git a/bsd-user/arm/target_arch_cpu.h b/bsd-user/arm/target_arch_cpu.h index c526fc7350..b7f728fd66 100644 --- a/bsd-user/arm/target_arch_cpu.h +++ b/bsd-user/arm/target_arch_cpu.h @@ -21,6 +21,7 @@ #define _TARGET_ARCH_CPU_H_ #include "target_arch.h" +#include "signal-common.h" #define TARGET_DEFAULT_CPU_MODEL "any" diff --git a/bsd-user/i386/target_arch_cpu.h b/bsd-user/i386/target_arch_cpu.h index b28602adbb..472a96689f 100644 --- a/bsd-user/i386/target_arch_cpu.h +++ b/bsd-user/i386/target_arch_cpu.h @@ -20,6 +20,7 @@ #define _TARGET_ARCH_CPU_H_ #include "target_arch.h" +#include "signal-common.h" #define TARGET_DEFAULT_CPU_MODEL "qemu32" diff --git a/bsd-user/qemu.h b/bsd-user/qemu.h index 671b26f00c..99c37fc994 100644 --- a/bsd-user/qemu.h +++ b/bsd-user/qemu.h @@ -199,14 +199,6 @@ print_openbsd_syscall(int num, void print_openbsd_syscall_ret(int num, abi_long ret); extern int do_strace; -/* signal.c */ -void process_pending_signals(CPUArchState *cpu_env); -void signal_init(void); -long do_sigreturn(CPUArchState *env); -long do_rt_sigreturn(CPUArchState *env); -void queue_signal(CPUArchState *env, int sig, target_siginfo_t *info); -abi_long do_sigaltstack(abi_ulong uss_addr, abi_ulong uoss_addr, abi_ulong sp); - /* mmap.c */ int target_mprotect(abi_ulong start, abi_ulong len, int prot); abi_long target_mmap(abi_ulong start, abi_ulong len, int prot, diff --git a/bsd-user/signal-common.h b/bsd-user/signal-common.h index 6207417d39..f9a9d1e01a 100644 --- a/bsd-user/signal-common.h +++ b/bsd-user/signal-common.h @@ -9,6 +9,12 @@ #ifndef SIGNAL_COMMON_H #define SIGNAL_COMMON_H +long do_rt_sigreturn(CPUArchState *env); +abi_long do_sigaltstack(abi_ulong uss_addr, abi_ulong uoss_addr, abi_ulong sp); +long do_sigreturn(CPUArchState *env); void force_sig_fault(int sig, int code, abi_ulong addr); +void process_pending_signals(CPUArchState *env); +void queue_signal(CPUArchState *env, int sig, target_siginfo_t *info); +void signal_init(void); #endif diff --git a/bsd-user/x86_64/target_arch_cpu.h b/bsd-user/x86_64/target_arch_cpu.h index 5172b230f0..14def48adb 100644 --- a/bsd-user/x86_64/target_arch_cpu.h +++ b/bsd-user/x86_64/target_arch_cpu.h @@ -20,6 +20,7 @@ #define _TARGET_ARCH_CPU_H_ #include "target_arch.h" +#include "signal-common.h" #define TARGET_DEFAULT_CPU_MODEL "qemu64" From fc9f9bdd3a6111d0bb419282657f89eea7d8de88 Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Sat, 8 Jan 2022 16:03:51 -0700 Subject: [PATCH 152/460] bsd-user/signal.c: Implement cpu_loop_exit_sigsegv First attempt at implementing cpu_loop_exit_sigsegv, mostly copied from linux-user version of this function. Signed-off-by: Stacey Son Signed-off-by: Kyle Evans Signed-off-by: Warner Losh Reviewed-by: Peter Maydell Reviewed-by: Richard Henderson --- bsd-user/signal.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/bsd-user/signal.c b/bsd-user/signal.c index 1206d0d728..12de0e2dea 100644 --- a/bsd-user/signal.c +++ b/bsd-user/signal.c @@ -20,6 +20,7 @@ #include "qemu/osdep.h" #include "qemu.h" #include "signal-common.h" +#include "hw/core/tcg-cpu-ops.h" /* * Stubbed out routines until we merge signal support from bsd-user @@ -63,9 +64,17 @@ void process_pending_signals(CPUArchState *cpu_env) void cpu_loop_exit_sigsegv(CPUState *cpu, target_ulong addr, MMUAccessType access_type, bool maperr, uintptr_t ra) { - qemu_log_mask(LOG_UNIMP, "No signal support for SIGSEGV\n"); - /* unreachable */ - abort(); + const struct TCGCPUOps *tcg_ops = CPU_GET_CLASS(cpu)->tcg_ops; + + if (tcg_ops->record_sigsegv) { + tcg_ops->record_sigsegv(cpu, addr, access_type, maperr, ra); + } + + force_sig_fault(TARGET_SIGSEGV, + maperr ? TARGET_SEGV_MAPERR : TARGET_SEGV_ACCERR, + addr); + cpu->exception_index = EXCP_INTERRUPT; + cpu_loop_exit_restore(cpu, ra); } void cpu_loop_exit_sigbus(CPUState *cpu, target_ulong addr, From cfdee273c4e41d0b485bc82966a82d6ab6f37a1d Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Sat, 8 Jan 2022 16:06:33 -0700 Subject: [PATCH 153/460] bsd-user/signal.c: implement cpu_loop_exit_sigbus First attempt at implementing cpu_loop_exit_sigbus, mostly copied from linux-user version of this function. Signed-off-by: Stacey Son Signed-off-by: Kyle Evans Signed-off-by: Warner Losh Reviewed-by: Peter Maydell Reviewed-by: Richard Henderson --- bsd-user/signal.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/bsd-user/signal.c b/bsd-user/signal.c index 12de0e2dea..844dfa1909 100644 --- a/bsd-user/signal.c +++ b/bsd-user/signal.c @@ -80,7 +80,13 @@ void cpu_loop_exit_sigsegv(CPUState *cpu, target_ulong addr, void cpu_loop_exit_sigbus(CPUState *cpu, target_ulong addr, MMUAccessType access_type, uintptr_t ra) { - qemu_log_mask(LOG_UNIMP, "No signal support for SIGBUS\n"); - /* unreachable */ - abort(); + const struct TCGCPUOps *tcg_ops = CPU_GET_CLASS(cpu)->tcg_ops; + + if (tcg_ops->record_sigbus) { + tcg_ops->record_sigbus(cpu, addr, access_type, ra); + } + + force_sig_fault(TARGET_SIGBUS, TARGET_BUS_ADRALN, addr); + cpu->exception_index = EXCP_INTERRUPT; + cpu_loop_exit_restore(cpu, ra); } From a3ed97cee57279620d934642c6da1eb0c5ae9df2 Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Sat, 8 Jan 2022 16:11:15 -0700 Subject: [PATCH 154/460] bsd-user/arm/arget_arch_cpu.h: Move EXCP_DEBUG and EXCP_BKPT together Implement EXCP_DEBUG and EXCP_BKPT the same, as is done in linux-user. The prior adjustment of register 15 isn't needed, so remove that. Remove a redunant comment (that code in FreeBSD never handled break points). It's unclear why BKPT was an alias for system calls, but FreeBSD doesn't do that today. Signed-off-by: Warner Losh Reviewed-by: Peter Maydell Reviewed-by: Richard Henderson --- bsd-user/arm/target_arch_cpu.h | 22 ++-------------------- 1 file changed, 2 insertions(+), 20 deletions(-) diff --git a/bsd-user/arm/target_arch_cpu.h b/bsd-user/arm/target_arch_cpu.h index b7f728fd66..05b19ce611 100644 --- a/bsd-user/arm/target_arch_cpu.h +++ b/bsd-user/arm/target_arch_cpu.h @@ -65,19 +65,7 @@ static inline void target_cpu_loop(CPUARMState *env) } break; case EXCP_SWI: - case EXCP_BKPT: { - /* - * system call - * See arm/arm/trap.c cpu_fetch_syscall_args() - */ - if (trapnr == EXCP_BKPT) { - if (env->thumb) { - env->regs[15] += 2; - } else { - env->regs[15] += 4; - } - } n = env->regs[7]; if (bsd_type == target_freebsd) { int ret; @@ -172,14 +160,8 @@ static inline void target_cpu_loop(CPUARMState *env) queue_signal(env, info.si_signo, &info); break; case EXCP_DEBUG: - { - - info.si_signo = TARGET_SIGTRAP; - info.si_errno = 0; - info.si_code = TARGET_TRAP_BRKPT; - info.si_addr = env->exception.vaddress; - queue_signal(env, info.si_signo, &info); - } + case EXCP_BKPT: + force_sig_fault(TARGET_SIGTRAP, TARGET_TRAP_BRKPT, env->regs[15]); break; case EXCP_YIELD: /* nothing to do here for user-mode, just resume guest code */ From c0b93df35248fcb842173abf583fe59d2b2692a5 Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Sat, 8 Jan 2022 16:14:04 -0700 Subject: [PATCH 155/460] bsd-user/arm/target_arch_cpu.h: Correct code pointer The code has moved in FreeBSD since the emulator was started, update the comment to reflect that change. Signed-off-by: Warner Losh Reviewed-by: Peter Maydell Reviewed-by: Richard Henderson --- bsd-user/arm/target_arch_cpu.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bsd-user/arm/target_arch_cpu.h b/bsd-user/arm/target_arch_cpu.h index 05b19ce611..905f13aa1b 100644 --- a/bsd-user/arm/target_arch_cpu.h +++ b/bsd-user/arm/target_arch_cpu.h @@ -73,7 +73,7 @@ static inline void target_cpu_loop(CPUARMState *env) int32_t syscall_nr = n; int32_t arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8; - /* See arm/arm/trap.c cpu_fetch_syscall_args() */ + /* See arm/arm/syscall.c cpu_fetch_syscall_args() */ if (syscall_nr == TARGET_FREEBSD_NR_syscall) { syscall_nr = env->regs[0]; arg1 = env->regs[1]; From 5e02ded157a7db45c3f06bd8c9d60f62d5bdeb1c Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Sat, 8 Jan 2022 16:22:16 -0700 Subject: [PATCH 156/460] bsd-user/arm/target_arch_cpu.h: Use force_sig_fault for EXCP_UDEF Use force_sig_fault to implement unknown opcode. This just uninlines that function, so simplify things by using it. Fold in EXCP_NOCP and EXCP_INVSTATE, as is done in linux-user. Make a note about slight differences with FreeBSD in case any of them turn out to be important later. Signed-off-by: Warner Losh Reviewed-by: Peter Maydell Reviewed-by: Richard Henderson --- bsd-user/arm/target_arch_cpu.h | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/bsd-user/arm/target_arch_cpu.h b/bsd-user/arm/target_arch_cpu.h index 905f13aa1b..9d79017642 100644 --- a/bsd-user/arm/target_arch_cpu.h +++ b/bsd-user/arm/target_arch_cpu.h @@ -51,18 +51,19 @@ static inline void target_cpu_loop(CPUARMState *env) process_queued_cpu_work(cs); switch (trapnr) { case EXCP_UDEF: - { - /* See arm/arm/undefined.c undefinedinstruction(); */ - info.si_addr = env->regs[15]; - - /* illegal instruction */ - info.si_signo = TARGET_SIGILL; - info.si_errno = 0; - info.si_code = TARGET_ILL_ILLOPC; - queue_signal(env, info.si_signo, &info); - - /* TODO: What about instruction emulation? */ - } + case EXCP_NOCP: + case EXCP_INVSTATE: + /* + * See arm/arm/undefined.c undefinedinstruction(); + * + * A number of details aren't emulated (they likely don't matter): + * o Misaligned PC generates ILL_ILLADR (these can't come from qemu) + * o Thumb-2 instructions generate ILLADR + * o Both modes implement coprocessor instructions, which we don't + * do here. FreeBSD just implements them for the VFP coprocessor + * and special kernel breakpoints, trace points, dtrace, etc. + */ + force_sig_fault(TARGET_SIGILL, TARGET_ILL_ILLOPC, env->regs[15]); break; case EXCP_SWI: { From 67ccbe798fef0912da54ecfddcf6ef5f0a02020b Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Sat, 8 Jan 2022 16:26:55 -0700 Subject: [PATCH 157/460] bsd-user/arm/target_arch_cpu.h: Implement data faults Update for the richer set of data faults that are now possible. Copied largely from linux-user/arm/cpu_loop.c, with minor typo fixes. Signed-off-by: Warner Losh Reviewed-by: Peter Maydell Reviewed-by: Richard Henderson --- bsd-user/arm/target_arch_cpu.h | 45 ++++++++++++++++++++++++++-------- 1 file changed, 35 insertions(+), 10 deletions(-) diff --git a/bsd-user/arm/target_arch_cpu.h b/bsd-user/arm/target_arch_cpu.h index 9d79017642..2b395d5c97 100644 --- a/bsd-user/arm/target_arch_cpu.h +++ b/bsd-user/arm/target_arch_cpu.h @@ -39,8 +39,7 @@ static inline void target_cpu_init(CPUARMState *env, static inline void target_cpu_loop(CPUARMState *env) { - int trapnr; - target_siginfo_t info; + int trapnr, si_signo, si_code; unsigned int n; CPUState *cs = env_cpu(env); @@ -150,15 +149,41 @@ static inline void target_cpu_loop(CPUARMState *env) /* just indicate that signals should be handled asap */ break; case EXCP_PREFETCH_ABORT: - /* See arm/arm/trap.c prefetch_abort_handler() */ case EXCP_DATA_ABORT: - /* See arm/arm/trap.c data_abort_handler() */ - info.si_signo = TARGET_SIGSEGV; - info.si_errno = 0; - /* XXX: check env->error_code */ - info.si_code = 0; - info.si_addr = env->exception.vaddress; - queue_signal(env, info.si_signo, &info); + /* + * See arm/arm/trap-v6.c prefetch_abort_handler() and + * data_abort_handler() + * + * However, FreeBSD maps these to a generic value and then uses that + * to maybe fault in pages in vm/vm_fault.c:vm_fault_trap(). I + * believe that the indirection maps the same as Linux, but haven't + * chased down every single possible indirection. + */ + + /* For user-only we don't set TTBCR_EAE, so look at the FSR. */ + switch (env->exception.fsr & 0x1f) { + case 0x1: /* Alignment */ + si_signo = TARGET_SIGBUS; + si_code = TARGET_BUS_ADRALN; + break; + case 0x3: /* Access flag fault, level 1 */ + case 0x6: /* Access flag fault, level 2 */ + case 0x9: /* Domain fault, level 1 */ + case 0xb: /* Domain fault, level 2 */ + case 0xd: /* Permission fault, level 1 */ + case 0xf: /* Permission fault, level 2 */ + si_signo = TARGET_SIGSEGV; + si_code = TARGET_SEGV_ACCERR; + break; + case 0x5: /* Translation fault, level 1 */ + case 0x7: /* Translation fault, level 2 */ + si_signo = TARGET_SIGSEGV; + si_code = TARGET_SEGV_MAPERR; + break; + default: + g_assert_not_reached(); + } + force_sig_fault(si_signo, si_code, env->exception.vaddress); break; case EXCP_DEBUG: case EXCP_BKPT: From 1366ef817a151f8f89a561494ea24204ad7917c7 Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Sat, 8 Jan 2022 16:48:03 -0700 Subject: [PATCH 158/460] bsd-user/signal.c: implement abstract target / host signal translation Implement host_to_target_signal and target_to_host_signal. Signed-off-by: Stacey Son Signed-off-by: Kyle Evans Signed-off-by: Warner Losh Reviewed-by: Peter Maydell Reviewed-by: Richard Henderson --- bsd-user/signal-common.h | 2 ++ bsd-user/signal.c | 16 ++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/bsd-user/signal-common.h b/bsd-user/signal-common.h index f9a9d1e01a..efed23d9ef 100644 --- a/bsd-user/signal-common.h +++ b/bsd-user/signal-common.h @@ -13,8 +13,10 @@ long do_rt_sigreturn(CPUArchState *env); abi_long do_sigaltstack(abi_ulong uss_addr, abi_ulong uoss_addr, abi_ulong sp); long do_sigreturn(CPUArchState *env); void force_sig_fault(int sig, int code, abi_ulong addr); +int host_to_target_signal(int sig); void process_pending_signals(CPUArchState *env); void queue_signal(CPUArchState *env, int sig, target_siginfo_t *info); void signal_init(void); +int target_to_host_signal(int sig); #endif diff --git a/bsd-user/signal.c b/bsd-user/signal.c index 844dfa1909..1313baec96 100644 --- a/bsd-user/signal.c +++ b/bsd-user/signal.c @@ -2,6 +2,7 @@ * Emulation of BSD signals * * Copyright (c) 2003 - 2008 Fabrice Bellard + * Copyright (c) 2013 Stacey Son * * 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 @@ -27,6 +28,21 @@ * fork. */ +/* + * The BSD ABIs use the same singal numbers across all the CPU architectures, so + * (unlike Linux) these functions are just the identity mapping. This might not + * be true for XyzBSD running on AbcBSD, which doesn't currently work. + */ +int host_to_target_signal(int sig) +{ + return sig; +} + +int target_to_host_signal(int sig) +{ + return sig; +} + /* * Queue a signal so that it will be send to the virtual CPU as soon as * possible. From 149076ade7b8250fa62a6b1e7462f8d2c340b27e Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Sat, 8 Jan 2022 16:57:31 -0700 Subject: [PATCH 159/460] bsd-user/signal.c: Implement signal_init() Initialize the signal state for the emulator. Setup a set of sane default signal handlers, mirroring the host's signals. For fatal signals (those that exit by default), establish our own set of signal handlers. Stub out the actual signal handler we use for the moment. Signed-off-by: Stacey Son Signed-off-by: Kyle Evans Signed-off-by: Warner Losh Reviewed-by: Peter Maydell Reviewed-by: Richard Henderson XXX SIGPROF PENDING --- bsd-user/qemu.h | 7 +++++ bsd-user/signal.c | 67 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+) diff --git a/bsd-user/qemu.h b/bsd-user/qemu.h index 99c37fc994..49f01932a5 100644 --- a/bsd-user/qemu.h +++ b/bsd-user/qemu.h @@ -94,6 +94,13 @@ typedef struct TaskState { * from multiple threads.) */ int signal_pending; + /* + * This thread's signal mask, as requested by the guest program. + * The actual signal mask of this thread may differ: + * + we don't let SIGSEGV and SIGBUS be blocked while running guest code + * + sometimes we block all signals to avoid races + */ + sigset_t signal_mask; uint8_t stack[]; } __attribute__((aligned(16))) TaskState; diff --git a/bsd-user/signal.c b/bsd-user/signal.c index 1313baec96..3ef7cf5e23 100644 --- a/bsd-user/signal.c +++ b/bsd-user/signal.c @@ -28,6 +28,9 @@ * fork. */ +static struct target_sigaction sigact_table[TARGET_NSIG]; +static void host_signal_handler(int host_sig, siginfo_t *info, void *puc); + /* * The BSD ABIs use the same singal numbers across all the CPU architectures, so * (unlike Linux) these functions are just the identity mapping. This might not @@ -52,6 +55,28 @@ void queue_signal(CPUArchState *env, int sig, target_siginfo_t *info) qemu_log_mask(LOG_UNIMP, "No signal queueing, dropping signal %d\n", sig); } +static int fatal_signal(int sig) +{ + + switch (sig) { + case TARGET_SIGCHLD: + case TARGET_SIGURG: + case TARGET_SIGWINCH: + case TARGET_SIGINFO: + /* Ignored by default. */ + return 0; + case TARGET_SIGCONT: + case TARGET_SIGSTOP: + case TARGET_SIGTSTP: + case TARGET_SIGTTIN: + case TARGET_SIGTTOU: + /* Job control signals. */ + return 0; + default: + return 1; + } +} + /* * Force a synchronously taken QEMU_SI_FAULT signal. For QEMU the * 'force' part is handled in process_pending_signals(). @@ -69,8 +94,50 @@ void force_sig_fault(int sig, int code, abi_ulong addr) queue_signal(env, sig, &info); } +static void host_signal_handler(int host_sig, siginfo_t *info, void *puc) +{ +} + void signal_init(void) { + TaskState *ts = (TaskState *)thread_cpu->opaque; + struct sigaction act; + struct sigaction oact; + int i; + int host_sig; + + /* Set the signal mask from the host mask. */ + sigprocmask(0, 0, &ts->signal_mask); + + sigfillset(&act.sa_mask); + act.sa_sigaction = host_signal_handler; + act.sa_flags = SA_SIGINFO; + + for (i = 1; i <= TARGET_NSIG; i++) { +#ifdef CONFIG_GPROF + if (i == TARGET_SIGPROF) { + continue; + } +#endif + host_sig = target_to_host_signal(i); + sigaction(host_sig, NULL, &oact); + if (oact.sa_sigaction == (void *)SIG_IGN) { + sigact_table[i - 1]._sa_handler = TARGET_SIG_IGN; + } else if (oact.sa_sigaction == (void *)SIG_DFL) { + sigact_table[i - 1]._sa_handler = TARGET_SIG_DFL; + } + /* + * If there's already a handler installed then something has + * gone horribly wrong, so don't even try to handle that case. + * Install some handlers for our own use. We need at least + * SIGSEGV and SIGBUS, to detect exceptions. We can not just + * trap all signals because it affects syscall interrupt + * behavior. But do trap all default-fatal signals. + */ + if (fatal_signal(i)) { + sigaction(host_sig, &act, NULL); + } + } } void process_pending_signals(CPUArchState *cpu_env) From e32a63010ff221f7e161a592972076d2976c5eae Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Sun, 16 Jan 2022 09:28:59 -0700 Subject: [PATCH 160/460] bsd-user/signal.c: Add si_type argument to queue_signal Mirror the linux-user practice and add a si_type argument to queue signal. This will be transported as the upper 8 bits in the si_type element of siginfo so that we know what bits of the structure are valid and so we can properly implement host_to_target_siginfo_noswap and tswap_siginfo. Adapt the one caller of queue_signal to the new interface. Use all the same names as Linux (except _RT which we don't treat differently, unlike Linux), though some are unused. Place this into signal-common.h since that's a better place given bsd-user's structure. Move prototype of queue_signal to signal-common.h to mirror linux-user's location. Signed-off-by: Warner Losh Reviewed-by: Richard Henderson --- bsd-user/signal-common.h | 26 +++++++++++++++++++++++++- bsd-user/signal.c | 5 +++-- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/bsd-user/signal-common.h b/bsd-user/signal-common.h index efed23d9ef..80e9503238 100644 --- a/bsd-user/signal-common.h +++ b/bsd-user/signal-common.h @@ -15,8 +15,32 @@ long do_sigreturn(CPUArchState *env); void force_sig_fault(int sig, int code, abi_ulong addr); int host_to_target_signal(int sig); void process_pending_signals(CPUArchState *env); -void queue_signal(CPUArchState *env, int sig, target_siginfo_t *info); +void queue_signal(CPUArchState *env, int sig, int si_type, + target_siginfo_t *info); void signal_init(void); int target_to_host_signal(int sig); +/* + * Within QEMU the top 8 bits of si_code indicate which of the parts of the + * union in target_siginfo is valid. This only applies between + * host_to_target_siginfo_noswap() and tswap_siginfo(); it does not appear + * either within host siginfo_t or in target_siginfo structures which we get + * from the guest userspace program. Linux kenrels use this internally, but BSD + * kernels don't do this, but its a useful abstraction. + * + * The linux-user version of this uses the top 16 bits, but FreeBSD's SI_USER + * and other signal indepenent SI_ codes have bit 16 set, so we only use the top + * byte instead. + * + * For FreeBSD, we have si_pid, si_uid, si_status, and si_addr always. Linux and + * {Open,Net}BSD have a different approach (where their reason field is larger, + * but whose siginfo has fewer fields always). + */ +#define QEMU_SI_NOINFO 0 /* nothing other than si_signo valid */ +#define QEMU_SI_FAULT 1 /* _fault is valid in _reason */ +#define QEMU_SI_TIMER 2 /* _timer is valid in _reason */ +#define QEMU_SI_MESGQ 3 /* _mesgq is valid in _reason */ +#define QEMU_SI_POLL 4 /* _poll is valid in _reason */ +#define QEMU_SI_CAPSICUM 5 /* _capsicum is valid in _reason */ + #endif diff --git a/bsd-user/signal.c b/bsd-user/signal.c index 3ef7cf5e23..ad8437a8bf 100644 --- a/bsd-user/signal.c +++ b/bsd-user/signal.c @@ -50,7 +50,8 @@ int target_to_host_signal(int sig) * Queue a signal so that it will be send to the virtual CPU as soon as * possible. */ -void queue_signal(CPUArchState *env, int sig, target_siginfo_t *info) +void queue_signal(CPUArchState *env, int sig, int si_type, + target_siginfo_t *info) { qemu_log_mask(LOG_UNIMP, "No signal queueing, dropping signal %d\n", sig); } @@ -91,7 +92,7 @@ void force_sig_fault(int sig, int code, abi_ulong addr) info.si_errno = 0; info.si_code = code; info.si_addr = addr; - queue_signal(env, sig, &info); + queue_signal(env, sig, QEMU_SI_FAULT, &info); } static void host_signal_handler(int host_sig, siginfo_t *info, void *puc) From 6e0bc06e210cbd25006c3a39e9a8325784d0be78 Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Sat, 8 Jan 2022 17:14:12 -0700 Subject: [PATCH 161/460] bsd-user/host/arm/host-signal.h: Implement host_signal_* Implement host_signal_pc, host_signal_set_pc and host_signal_write for arm. Signed-off-by: Kyle Evans Signed-off-by: Warner Losh Reviewed-by: Richard Henderson --- bsd-user/host/arm/host-signal.h | 35 +++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 bsd-user/host/arm/host-signal.h diff --git a/bsd-user/host/arm/host-signal.h b/bsd-user/host/arm/host-signal.h new file mode 100644 index 0000000000..56679bd699 --- /dev/null +++ b/bsd-user/host/arm/host-signal.h @@ -0,0 +1,35 @@ +/* + * host-signal.h: signal info dependent on the host architecture + * + * Copyright (c) 2021 Warner Losh + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef ARM_HOST_SIGNAL_H +#define ARM_HOST_SIGNAL_H + +#include + +static inline uintptr_t host_signal_pc(ucontext_t *uc) +{ + return uc->uc_mcontext.__gregs[_REG_PC]; +} + +static inline void host_signal_set_pc(ucontext_t *uc, uintptr_t pc) +{ + uc->uc_mcontext.__gregs[_REG_PC] = pc; +} + +static inline bool host_signal_write(siginfo_t *info, ucontext_t *uc) +{ + /* + * In the FSR, bit 11 is WnR. FreeBSD returns this as part of the + * si_info.si_trapno. + */ + uint32_t fsr = info->si_trapno; + + return extract32(fsr, 11, 1); +} + +#endif From 220f8606c8d48e5d6d4145abccebb0fa8518c507 Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Sat, 8 Jan 2022 17:15:19 -0700 Subject: [PATCH 162/460] bsd-user/host/i386/host-signal.h: Implement host_signal_* Implement host_signal_pc, host_signal_set_pc and host_signal_write for i386. Signed-off-by: Kyle Evans Signed-off-by: Warner Losh Reviewed-by: Peter Maydell Reviewed-by: Richard Henderson --- bsd-user/host/i386/host-signal.h | 37 ++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 bsd-user/host/i386/host-signal.h diff --git a/bsd-user/host/i386/host-signal.h b/bsd-user/host/i386/host-signal.h new file mode 100644 index 0000000000..169e61b154 --- /dev/null +++ b/bsd-user/host/i386/host-signal.h @@ -0,0 +1,37 @@ +/* + * host-signal.h: signal info dependent on the host architecture + * + * Copyright (c) 2021 Warner Losh + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef I386_HOST_SIGNAL_H +#define I386_HOST_SIGNAL_H + +#include +#include +#include +#include + +static inline uintptr_t host_signal_pc(ucontext_t *uc) +{ + return uc->uc_mcontext.mc_eip; +} + +static inline void host_signal_set_pc(ucontext_t *uc, uintptr_t pc) +{ + uc->uc_mcontext.mc_eip = pc; +} + +static inline bool host_signal_write(siginfo_t *info, ucontext_t *uc) +{ + /* + * Look in sys/i386/i386/trap.c. NOTE: mc_err == tr_err due to type punning + * between a trapframe and mcontext on FreeBSD/i386. + */ + return uc->uc_mcontext.mc_trapno == T_PAGEFLT && + uc->uc_mcontext.mc_err & PGEX_W; +} + +#endif From b375158801e804cfbf2ff45edab1bd7590fdad30 Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Sat, 8 Jan 2022 17:15:57 -0700 Subject: [PATCH 163/460] bsd-user/host/x86_64/host-signal.h: Implement host_signal_* Implement host_signal_pc, host_signal_set_pc and host_signal_write for x86_64. Signed-off-by: Kyle Evans Signed-off-by: Warner Losh Reviewed-by: Peter Maydell Reviewed-by: Richard Henderson --- bsd-user/host/x86_64/host-signal.h | 37 ++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 bsd-user/host/x86_64/host-signal.h diff --git a/bsd-user/host/x86_64/host-signal.h b/bsd-user/host/x86_64/host-signal.h new file mode 100644 index 0000000000..47ca19f881 --- /dev/null +++ b/bsd-user/host/x86_64/host-signal.h @@ -0,0 +1,37 @@ +/* + * host-signal.h: signal info dependent on the host architecture + * + * Copyright (c) 2021 Warner Losh + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef X86_64_HOST_SIGNAL_H +#define X86_64_HOST_SIGNAL_H + +#include +#include +#include +#include + +static inline uintptr_t host_signal_pc(ucontext_t *uc) +{ + return uc->uc_mcontext.mc_rip; +} + +static inline void host_signal_set_pc(ucontext_t *uc, uintptr_t pc) +{ + uc->uc_mcontext.mc_rip = pc; +} + +static inline bool host_signal_write(siginfo_t *info, ucontext_t *uc) +{ + /* + * Look in sys/amd64/amd64/trap.c. NOTE: mc_err == tr_err due to type + * punning between a trapframe and mcontext on FreeBSD/amd64. + */ + return uc->uc_mcontext.mc_trapno == T_PAGEFLT && + uc->uc_mcontext.mc_err & PGEX_W; +} + +#endif From 113b727ce788335cf76f65355d670c9bc130fd75 Mon Sep 17 00:00:00 2001 From: Hanna Reitz Date: Tue, 18 Jan 2022 17:59:59 +0100 Subject: [PATCH 164/460] block/io: Update BSC only if want_zero is true We update the block-status cache whenever we get new information from a bdrv_co_block_status() call to the block driver. However, if we have passed want_zero=false to that call, it may flag areas containing zeroes as data, and so we would update the block-status cache with wrong information. Therefore, we should not update the cache with want_zero=false. Reported-by: Nir Soffer Fixes: 0bc329fbb00 ("block: block-status cache for data regions") Reviewed-by: Nir Soffer Cc: qemu-stable@nongnu.org Signed-off-by: Hanna Reitz Message-Id: <20220118170000.49423-2-hreitz@redhat.com> Reviewed-by: Eric Blake Signed-off-by: Eric Blake --- block/io.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/block/io.c b/block/io.c index bb0a254def..4e4cb556c5 100644 --- a/block/io.c +++ b/block/io.c @@ -2497,8 +2497,12 @@ static int coroutine_fn bdrv_co_block_status(BlockDriverState *bs, * non-protocol nodes, and then it is never used. However, filling * the cache requires an RCU update, so double check here to avoid * such an update if possible. + * + * Check want_zero, because we only want to update the cache when we + * have accurate information about what is zero and what is data. */ - if (ret == (BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID) && + if (want_zero && + ret == (BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID) && QLIST_EMPTY(&bs->children)) { /* From 85fc1b5dbf893254471809eef8ec773bb29d4f48 Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Sat, 8 Jan 2022 17:27:34 -0700 Subject: [PATCH 165/460] bsd-user: Add host signals to the build Start to add the host signal functionality to the build. Signed-off-by: Warner Losh Reviewed-by: Peter Maydell Reviewed-by: Richard Henderson --- bsd-user/signal.c | 1 + meson.build | 1 + 2 files changed, 2 insertions(+) diff --git a/bsd-user/signal.c b/bsd-user/signal.c index ad8437a8bf..f3e020e004 100644 --- a/bsd-user/signal.c +++ b/bsd-user/signal.c @@ -22,6 +22,7 @@ #include "qemu.h" #include "signal-common.h" #include "hw/core/tcg-cpu-ops.h" +#include "host-signal.h" /* * Stubbed out routines until we merge signal support from bsd-user diff --git a/meson.build b/meson.build index 5dbc9a7a36..155403d44f 100644 --- a/meson.build +++ b/meson.build @@ -2947,6 +2947,7 @@ foreach target : target_dirs if 'CONFIG_BSD_USER' in config_target base_dir = 'bsd-user' target_inc += include_directories('bsd-user/' / targetos) + target_inc += include_directories('bsd-user/host/' / host_arch) dir = base_dir / abi arch_srcs += files(dir / 'signal.c', dir / 'target_arch_cpu.c') endif From 6ddc1abe0fd1099f807b27306b518752ea3f40e0 Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Sat, 8 Jan 2022 17:37:23 -0700 Subject: [PATCH 166/460] bsd-user: Add trace events for bsd-user Add the bsd-user specific events and infrastructure. Only include the linux-user trace events for linux-user, not bsd-user. Signed-off-by: Stacey Son Signed-off-by: Kyle Evans Signed-off-by: Warner Losh Reviewed-by: Peter Maydell Reviewed-by: Richard Henderson --- bsd-user/signal.c | 1 + bsd-user/trace-events | 11 +++++++++++ bsd-user/trace.h | 1 + meson.build | 5 ++++- 4 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 bsd-user/trace-events create mode 100644 bsd-user/trace.h diff --git a/bsd-user/signal.c b/bsd-user/signal.c index f3e020e004..cb0036acb6 100644 --- a/bsd-user/signal.c +++ b/bsd-user/signal.c @@ -21,6 +21,7 @@ #include "qemu/osdep.h" #include "qemu.h" #include "signal-common.h" +#include "trace.h" #include "hw/core/tcg-cpu-ops.h" #include "host-signal.h" diff --git a/bsd-user/trace-events b/bsd-user/trace-events new file mode 100644 index 0000000000..843896f627 --- /dev/null +++ b/bsd-user/trace-events @@ -0,0 +1,11 @@ +# See docs/tracing.txt for syntax documentation. + +# bsd-user/signal.c +user_setup_frame(void *env, uint64_t frame_addr) "env=%p frame_addr=0x%"PRIx64 +user_setup_rt_frame(void *env, uint64_t frame_addr) "env=%p frame_addr=0x%"PRIx64 +user_do_rt_sigreturn(void *env, uint64_t frame_addr) "env=%p frame_addr=0x%"PRIx64 +user_do_sigreturn(void *env, uint64_t frame_addr) "env=%p frame_addr=0x%"PRIx64 +user_dump_core_and_abort(void *env, int target_sig, int host_sig) "env=%p signal %d (host %d)" +user_handle_signal(void *env, int target_sig) "env=%p signal %d" +user_host_signal(void *env, int host_sig, int target_sig) "env=%p signal %d (target %d(" +user_queue_signal(void *env, int target_sig) "env=%p signal %d" diff --git a/bsd-user/trace.h b/bsd-user/trace.h new file mode 100644 index 0000000000..593c0204ad --- /dev/null +++ b/bsd-user/trace.h @@ -0,0 +1 @@ +#include "trace/trace-bsd_user.h" diff --git a/meson.build b/meson.build index 155403d44f..5f43355071 100644 --- a/meson.build +++ b/meson.build @@ -2458,9 +2458,12 @@ trace_events_subdirs = [ 'monitor', 'util', ] -if have_user +if have_linux_user trace_events_subdirs += [ 'linux-user' ] endif +if have_bsd_user + trace_events_subdirs += [ 'bsd-user' ] +endif if have_block trace_events_subdirs += [ 'authz', From c34f2aaff645cbda1d69f71b8fa9173fec5b1a8d Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Sat, 8 Jan 2022 17:50:38 -0700 Subject: [PATCH 167/460] bsd-user/signal.c: host_to_target_siginfo_noswap Implement conversion of host to target siginfo. Signed-off-by: Stacey Son Signed-off-by: Kyle Evans Signed-off-by: Warner Losh Reviewed-by: Richard Henderson --- bsd-user/signal.c | 113 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 113 insertions(+) diff --git a/bsd-user/signal.c b/bsd-user/signal.c index cb0036acb6..db8cf0a08f 100644 --- a/bsd-user/signal.c +++ b/bsd-user/signal.c @@ -48,6 +48,119 @@ int target_to_host_signal(int sig) return sig; } +static bool has_trapno(int tsig) +{ + return tsig == TARGET_SIGILL || + tsig == TARGET_SIGFPE || + tsig == TARGET_SIGSEGV || + tsig == TARGET_SIGBUS || + tsig == TARGET_SIGTRAP; +} + + +/* Siginfo conversion. */ + +/* + * Populate tinfo w/o swapping based on guessing which fields are valid. + */ +static inline void host_to_target_siginfo_noswap(target_siginfo_t *tinfo, + const siginfo_t *info) +{ + int sig = host_to_target_signal(info->si_signo); + int si_code = info->si_code; + int si_type; + + /* + * Make sure we that the variable portion of the target siginfo is zeroed + * out so we don't leak anything into that. + */ + memset(&tinfo->_reason, 0, sizeof(tinfo->_reason)); + + /* + * This is awkward, because we have to use a combination of the si_code and + * si_signo to figure out which of the union's members are valid.o We + * therefore make our best guess. + * + * Once we have made our guess, we record it in the top 16 bits of + * the si_code, so that tswap_siginfo() later can use it. + * tswap_siginfo() will strip these top bits out before writing + * si_code to the guest (sign-extending the lower bits). + */ + tinfo->si_signo = sig; + tinfo->si_errno = info->si_errno; + tinfo->si_code = info->si_code; + tinfo->si_pid = info->si_pid; + tinfo->si_uid = info->si_uid; + tinfo->si_status = info->si_status; + tinfo->si_addr = (abi_ulong)(unsigned long)info->si_addr; + /* + * si_value is opaque to kernel. On all FreeBSD platforms, + * sizeof(sival_ptr) >= sizeof(sival_int) so the following + * always will copy the larger element. + */ + tinfo->si_value.sival_ptr = + (abi_ulong)(unsigned long)info->si_value.sival_ptr; + + switch (si_code) { + /* + * All the SI_xxx codes that are defined here are global to + * all the signals (they have values that none of the other, + * more specific signal info will set). + */ + case SI_USER: + case SI_LWP: + case SI_KERNEL: + case SI_QUEUE: + case SI_ASYNCIO: + /* + * Only the fixed parts are valid (though FreeBSD doesn't always + * set all the fields to non-zero values. + */ + si_type = QEMU_SI_NOINFO; + break; + case SI_TIMER: + tinfo->_reason._timer._timerid = info->_reason._timer._timerid; + tinfo->_reason._timer._overrun = info->_reason._timer._overrun; + si_type = QEMU_SI_TIMER; + break; + case SI_MESGQ: + tinfo->_reason._mesgq._mqd = info->_reason._mesgq._mqd; + si_type = QEMU_SI_MESGQ; + break; + default: + /* + * We have to go based on the signal number now to figure out + * what's valid. + */ + if (has_trapno(sig)) { + tinfo->_reason._fault._trapno = info->_reason._fault._trapno; + si_type = QEMU_SI_FAULT; + } +#ifdef TARGET_SIGPOLL + /* + * FreeBSD never had SIGPOLL, but emulates it for Linux so there's + * a chance it may popup in the future. + */ + if (sig == TARGET_SIGPOLL) { + tinfo->_reason._poll._band = info->_reason._poll._band; + si_type = QEMU_SI_POLL; + } +#endif + /* + * Unsure that this can actually be generated, and our support for + * capsicum is somewhere between weak and non-existant, but if we get + * one, then we know what to save. + */ + if (sig == TARGET_SIGTRAP) { + tinfo->_reason._capsicum._syscall = + info->_reason._capsicum._syscall; + si_type = QEMU_SI_CAPSICUM; + } + break; + } + tinfo->si_code = deposit32(si_code, 24, 8, si_type); +} + /* * Queue a signal so that it will be send to the virtual CPU as soon as * possible. From aae57ac37a8803cdd39a732491718b6ee772bb3d Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Sat, 8 Jan 2022 17:55:56 -0700 Subject: [PATCH 168/460] bsd-user/signal.c: Implement rewind_if_in_safe_syscall Signed-off-by: Kyle Evans Signed-off-by: Warner Losh Reviewed-by: Peter Maydell Reviewed-by: Richard Henderson --- bsd-user/qemu.h | 2 ++ bsd-user/signal.c | 13 ++++++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/bsd-user/qemu.h b/bsd-user/qemu.h index 49f01932a5..8ed1bfbca8 100644 --- a/bsd-user/qemu.h +++ b/bsd-user/qemu.h @@ -446,4 +446,6 @@ static inline void *lock_user_string(abi_ulong guest_addr) #include +#include "user/safe-syscall.h" + #endif /* QEMU_H */ diff --git a/bsd-user/signal.c b/bsd-user/signal.c index db8cf0a08f..454aef2993 100644 --- a/bsd-user/signal.c +++ b/bsd-user/signal.c @@ -48,6 +48,18 @@ int target_to_host_signal(int sig) return sig; } +/* Adjust the signal context to rewind out of safe-syscall if we're in it */ +static inline void rewind_if_in_safe_syscall(void *puc) +{ + ucontext_t *uc = (ucontext_t *)puc; + uintptr_t pcreg = host_signal_pc(uc); + + if (pcreg > (uintptr_t)safe_syscall_start + && pcreg < (uintptr_t)safe_syscall_end) { + host_signal_set_pc(uc, (uintptr_t)safe_syscall_start); + } +} + static bool has_trapno(int tsig) { return tsig == TARGET_SIGILL || @@ -57,7 +69,6 @@ static bool has_trapno(int tsig) tsig == TARGET_SIGTRAP; } - /* Siginfo conversion. */ /* From e625c7ef5c07431a708f9fb0d98cbfeea1ad3ccc Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Sat, 8 Jan 2022 17:57:34 -0700 Subject: [PATCH 169/460] bsd-user/signal.c: Implement host_signal_handler Implement host_signal_handler to handle signals generated by the host and to do safe system calls. Signed-off-by: Stacey Son Signed-off-by: Kyle Evans Signed-off-by: Warner Losh Reviewed-by: Richard Henderson --- bsd-user/signal.c | 105 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) diff --git a/bsd-user/signal.c b/bsd-user/signal.c index 454aef2993..24cf4b1120 100644 --- a/bsd-user/signal.c +++ b/bsd-user/signal.c @@ -223,6 +223,111 @@ void force_sig_fault(int sig, int code, abi_ulong addr) static void host_signal_handler(int host_sig, siginfo_t *info, void *puc) { + CPUArchState *env = thread_cpu->env_ptr; + CPUState *cpu = env_cpu(env); + TaskState *ts = cpu->opaque; + target_siginfo_t tinfo; + ucontext_t *uc = puc; + struct emulated_sigtable *k; + int guest_sig; + uintptr_t pc = 0; + bool sync_sig = false; + + /* + * Non-spoofed SIGSEGV and SIGBUS are synchronous, and need special + * handling wrt signal blocking and unwinding. + */ + if ((host_sig == SIGSEGV || host_sig == SIGBUS) && info->si_code > 0) { + MMUAccessType access_type; + uintptr_t host_addr; + abi_ptr guest_addr; + bool is_write; + + host_addr = (uintptr_t)info->si_addr; + + /* + * Convert forcefully to guest address space: addresses outside + * reserved_va are still valid to report via SEGV_MAPERR. + */ + guest_addr = h2g_nocheck(host_addr); + + pc = host_signal_pc(uc); + is_write = host_signal_write(info, uc); + access_type = adjust_signal_pc(&pc, is_write); + + if (host_sig == SIGSEGV) { + bool maperr = true; + + if (info->si_code == SEGV_ACCERR && h2g_valid(host_addr)) { + /* If this was a write to a TB protected page, restart. */ + if (is_write && + handle_sigsegv_accerr_write(cpu, &uc->uc_sigmask, + pc, guest_addr)) { + return; + } + + /* + * With reserved_va, the whole address space is PROT_NONE, + * which means that we may get ACCERR when we want MAPERR. + */ + if (page_get_flags(guest_addr) & PAGE_VALID) { + maperr = false; + } else { + info->si_code = SEGV_MAPERR; + } + } + + sigprocmask(SIG_SETMASK, &uc->uc_sigmask, NULL); + cpu_loop_exit_sigsegv(cpu, guest_addr, access_type, maperr, pc); + } else { + sigprocmask(SIG_SETMASK, &uc->uc_sigmask, NULL); + if (info->si_code == BUS_ADRALN) { + cpu_loop_exit_sigbus(cpu, guest_addr, access_type, pc); + } + } + + sync_sig = true; + } + + /* Get the target signal number. */ + guest_sig = host_to_target_signal(host_sig); + if (guest_sig < 1 || guest_sig > TARGET_NSIG) { + return; + } + trace_user_host_signal(cpu, host_sig, guest_sig); + + host_to_target_siginfo_noswap(&tinfo, info); + + k = &ts->sigtab[guest_sig - 1]; + k->info = tinfo; + k->pending = guest_sig; + ts->signal_pending = 1; + + /* + * For synchronous signals, unwind the cpu state to the faulting + * insn and then exit back to the main loop so that the signal + * is delivered immediately. + */ + if (sync_sig) { + cpu->exception_index = EXCP_INTERRUPT; + cpu_loop_exit_restore(cpu, pc); + } + + rewind_if_in_safe_syscall(puc); + + /* + * Block host signals until target signal handler entered. We + * can't block SIGSEGV or SIGBUS while we're executing guest + * code in case the guest code provokes one in the window between + * now and it getting out to the main loop. Signals will be + * unblocked again in process_pending_signals(). + */ + sigfillset(&uc->uc_sigmask); + sigdelset(&uc->uc_sigmask, SIGSEGV); + sigdelset(&uc->uc_sigmask, SIGBUS); + + /* Interrupt the virtual CPU as soon as possible. */ + cpu_exit(thread_cpu); } void signal_init(void) From fd5bec9ad28eaa454feaad46e68a76b9eeedb3ff Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Sat, 8 Jan 2022 20:04:18 -0700 Subject: [PATCH 170/460] bsd-user/strace.c: print_taken_signal print_taken_signal() prints signals when we're tracing signals. Signed-off-by: Stacey Son Signed-off-by: Kyle Evans Signed-off-by: Warner Losh Reviewed-by: Peter Maydell Reviewed-by: Richard Henderson --- bsd-user/qemu.h | 10 +++++ bsd-user/strace.c | 97 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 107 insertions(+) diff --git a/bsd-user/qemu.h b/bsd-user/qemu.h index 8ed1bfbca8..a7964776fd 100644 --- a/bsd-user/qemu.h +++ b/bsd-user/qemu.h @@ -204,6 +204,16 @@ print_openbsd_syscall(int num, abi_long arg1, abi_long arg2, abi_long arg3, abi_long arg4, abi_long arg5, abi_long arg6); void print_openbsd_syscall_ret(int num, abi_long ret); +/** + * print_taken_signal: + * @target_signum: target signal being taken + * @tinfo: target_siginfo_t which will be passed to the guest for the signal + * + * Print strace output indicating that this signal is being taken by the guest, + * in a format similar to: + * --- SIGSEGV {si_signo=SIGSEGV, si_code=SI_KERNEL, si_addr=0} --- + */ +void print_taken_signal(int target_signum, const target_siginfo_t *tinfo); extern int do_strace; /* mmap.c */ diff --git a/bsd-user/strace.c b/bsd-user/strace.c index be40b8a20c..a77d10dd6b 100644 --- a/bsd-user/strace.c +++ b/bsd-user/strace.c @@ -31,6 +31,24 @@ int do_strace; /* * Utility functions */ +static const char * +get_comma(int last) +{ + return (last) ? "" : ","; +} + +/* + * Prints out raw parameter using given format. Caller needs + * to do byte swapping if needed. + */ +static void +print_raw_param(const char *fmt, abi_long param, int last) +{ + char format[64]; + + (void)snprintf(format, sizeof(format), "%s%s", fmt, get_comma(last)); + gemu_log(format, param); +} static void print_sysctl(const struct syscallname *name, abi_long arg1, abi_long arg2, abi_long arg3, abi_long arg4, abi_long arg5, @@ -239,3 +257,82 @@ void print_openbsd_syscall_ret(int num, abi_long ret) print_syscall_ret(num, ret, openbsd_scnames, ARRAY_SIZE(openbsd_scnames)); } + +static void +print_signal(abi_ulong arg, int last) +{ + const char *signal_name = NULL; + switch (arg) { + case TARGET_SIGHUP: + signal_name = "SIGHUP"; + break; + case TARGET_SIGINT: + signal_name = "SIGINT"; + break; + case TARGET_SIGQUIT: + signal_name = "SIGQUIT"; + break; + case TARGET_SIGILL: + signal_name = "SIGILL"; + break; + case TARGET_SIGABRT: + signal_name = "SIGABRT"; + break; + case TARGET_SIGFPE: + signal_name = "SIGFPE"; + break; + case TARGET_SIGKILL: + signal_name = "SIGKILL"; + break; + case TARGET_SIGSEGV: + signal_name = "SIGSEGV"; + break; + case TARGET_SIGPIPE: + signal_name = "SIGPIPE"; + break; + case TARGET_SIGALRM: + signal_name = "SIGALRM"; + break; + case TARGET_SIGTERM: + signal_name = "SIGTERM"; + break; + case TARGET_SIGUSR1: + signal_name = "SIGUSR1"; + break; + case TARGET_SIGUSR2: + signal_name = "SIGUSR2"; + break; + case TARGET_SIGCHLD: + signal_name = "SIGCHLD"; + break; + case TARGET_SIGCONT: + signal_name = "SIGCONT"; + break; + case TARGET_SIGSTOP: + signal_name = "SIGSTOP"; + break; + case TARGET_SIGTTIN: + signal_name = "SIGTTIN"; + break; + case TARGET_SIGTTOU: + signal_name = "SIGTTOU"; + break; + } + if (signal_name == NULL) { + print_raw_param("%ld", arg, last); + return; + } + gemu_log("%s%s", signal_name, get_comma(last)); +} + +void print_taken_signal(int target_signum, const target_siginfo_t *tinfo) +{ + /* + * Print the strace output for a signal being taken: + * --- SIGSEGV {si_signo=SIGSEGV, si_code=SI_KERNEL, si_addr=0} --- + */ + gemu_log("%d ", getpid()); + gemu_log("--- "); + print_signal(target_signum, 1); + gemu_log(" ---\n"); +} From 377145478339917491a850643bb920548907d956 Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Sat, 8 Jan 2022 21:05:20 -0700 Subject: [PATCH 171/460] bsd-user/signal.c: Implement dump_core_and_abort Force delivering a signal and generating a core file. It's a global function for the moment... Signed-off-by: Stacey Son Signed-off-by: Kyle Evans Signed-off-by: Warner Losh Reviewed-by: Richard Henderson --- bsd-user/signal.c | 76 +++++++++++++++++++++++++++++++++++++++++ bsd-user/syscall_defs.h | 1 + 2 files changed, 77 insertions(+) diff --git a/bsd-user/signal.c b/bsd-user/signal.c index 24cf4b1120..ccda7adbee 100644 --- a/bsd-user/signal.c +++ b/bsd-user/signal.c @@ -172,6 +172,82 @@ static inline void host_to_target_siginfo_noswap(target_siginfo_t *tinfo, tinfo->si_code = deposit32(si_code, 24, 8, si_type); } +/* Returns 1 if given signal should dump core if not handled. */ +static int core_dump_signal(int sig) +{ + switch (sig) { + case TARGET_SIGABRT: + case TARGET_SIGFPE: + case TARGET_SIGILL: + case TARGET_SIGQUIT: + case TARGET_SIGSEGV: + case TARGET_SIGTRAP: + case TARGET_SIGBUS: + return 1; + default: + return 0; + } +} + +/* Abort execution with signal. */ +static void QEMU_NORETURN dump_core_and_abort(int target_sig) +{ + CPUArchState *env = thread_cpu->env_ptr; + CPUState *cpu = env_cpu(env); + TaskState *ts = cpu->opaque; + int core_dumped = 0; + int host_sig; + struct sigaction act; + + host_sig = target_to_host_signal(target_sig); + gdb_signalled(env, target_sig); + + /* Dump core if supported by target binary format */ + if (core_dump_signal(target_sig) && (ts->bprm->core_dump != NULL)) { + stop_all_tasks(); + core_dumped = + ((*ts->bprm->core_dump)(target_sig, env) == 0); + } + if (core_dumped) { + struct rlimit nodump; + + /* + * We already dumped the core of target process, we don't want + * a coredump of qemu itself. + */ + getrlimit(RLIMIT_CORE, &nodump); + nodump.rlim_cur = 0; + setrlimit(RLIMIT_CORE, &nodump); + (void) fprintf(stderr, "qemu: uncaught target signal %d (%s) " + "- %s\n", target_sig, strsignal(host_sig), "core dumped"); + } + + /* + * The proper exit code for dying from an uncaught signal is + * -. The kernel doesn't allow exit() or _exit() to pass + * a negative value. To get the proper exit code we need to + * actually die from an uncaught signal. Here the default signal + * handler is installed, we send ourself a signal and we wait for + * it to arrive. + */ + memset(&act, 0, sizeof(act)); + sigfillset(&act.sa_mask); + act.sa_handler = SIG_DFL; + sigaction(host_sig, &act, NULL); + + kill(getpid(), host_sig); + + /* + * Make sure the signal isn't masked (just reuse the mask inside + * of act). + */ + sigdelset(&act.sa_mask, host_sig); + sigsuspend(&act.sa_mask); + + /* unreachable */ + abort(); +} + /* * Queue a signal so that it will be send to the virtual CPU as soon as * possible. diff --git a/bsd-user/syscall_defs.h b/bsd-user/syscall_defs.h index 04a1a886d7..62b472b990 100644 --- a/bsd-user/syscall_defs.h +++ b/bsd-user/syscall_defs.h @@ -21,6 +21,7 @@ #define _SYSCALL_DEFS_H_ #include +#include #include "errno_defs.h" From 38be620c950dcf629ba3217c6a183fee0e790fa8 Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Sat, 8 Jan 2022 21:15:11 -0700 Subject: [PATCH 172/460] bsd-user/signal.c: Fill in queue_signal Fill in queue signal implementation, as well as routines allocate and delete elements of the signal queue. Signed-off-by: Stacey Son Signed-off-by: Kyle Evans Signed-off-by: Warner Losh Reviewed-by: Richard Henderson --- bsd-user/qemu.h | 5 +++++ bsd-user/signal.c | 13 ++++++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/bsd-user/qemu.h b/bsd-user/qemu.h index a7964776fd..1648a509b9 100644 --- a/bsd-user/qemu.h +++ b/bsd-user/qemu.h @@ -85,6 +85,11 @@ typedef struct TaskState { struct bsd_binprm *bprm; struct image_info *info; + struct emulated_sigtable sync_signal; + /* + * TODO: Since we block all signals while returning to the main CPU + * loop, this needn't be an array + */ struct emulated_sigtable sigtab[TARGET_NSIG]; /* * Nonzero if process_pending_signals() needs to do something (either diff --git a/bsd-user/signal.c b/bsd-user/signal.c index ccda7adbee..34663f7a28 100644 --- a/bsd-user/signal.c +++ b/bsd-user/signal.c @@ -255,7 +255,18 @@ static void QEMU_NORETURN dump_core_and_abort(int target_sig) void queue_signal(CPUArchState *env, int sig, int si_type, target_siginfo_t *info) { - qemu_log_mask(LOG_UNIMP, "No signal queueing, dropping signal %d\n", sig); + CPUState *cpu = env_cpu(env); + TaskState *ts = cpu->opaque; + + trace_user_queue_signal(env, sig); + + info->si_code = deposit32(info->si_code, 24, 8, si_type); + + ts->sync_signal.info = *info; + ts->sync_signal.pending = sig; + /* Signal that a new signal is pending. */ + qatomic_set(&ts->signal_pending, 1); + return; } static int fatal_signal(int sig) From c93cbac1f4aab40c3fd4ac7488c7e3365ec5c894 Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Sat, 8 Jan 2022 21:24:18 -0700 Subject: [PATCH 173/460] bsd-user/signal.c: sigset manipulation routines. target_sigemptyset: resets a set to having no bits set target_sigaddset: adds a signal to a set target_sigismember: returns true when signal is a member host_to_target_sigset_internal: convert host sigset to target host_to_target_sigset: convert host sigset to target target_to_host_sigset_internal: convert target sigset to host target_to_host_sigset: convert target sigset to host Signed-off-by: Stacey Son Signed-off-by: Kyle Evans Signed-off-by: Warner Losh Reviewed-by: Richard Henderson --- bsd-user/signal-common.h | 2 ++ bsd-user/signal.c | 74 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+) diff --git a/bsd-user/signal-common.h b/bsd-user/signal-common.h index 80e9503238..ee819266f5 100644 --- a/bsd-user/signal-common.h +++ b/bsd-user/signal-common.h @@ -14,11 +14,13 @@ abi_long do_sigaltstack(abi_ulong uss_addr, abi_ulong uoss_addr, abi_ulong sp); long do_sigreturn(CPUArchState *env); void force_sig_fault(int sig, int code, abi_ulong addr); int host_to_target_signal(int sig); +void host_to_target_sigset(target_sigset_t *d, const sigset_t *s); void process_pending_signals(CPUArchState *env); void queue_signal(CPUArchState *env, int sig, int si_type, target_siginfo_t *info); void signal_init(void); int target_to_host_signal(int sig); +void target_to_host_sigset(sigset_t *d, const target_sigset_t *s); /* * Within QEMU the top 8 bits of si_code indicate which of the parts of the diff --git a/bsd-user/signal.c b/bsd-user/signal.c index 34663f7a28..84dafa4e9f 100644 --- a/bsd-user/signal.c +++ b/bsd-user/signal.c @@ -32,6 +32,9 @@ static struct target_sigaction sigact_table[TARGET_NSIG]; static void host_signal_handler(int host_sig, siginfo_t *info, void *puc); +static void target_to_host_sigset_internal(sigset_t *d, + const target_sigset_t *s); + /* * The BSD ABIs use the same singal numbers across all the CPU architectures, so @@ -48,6 +51,25 @@ int target_to_host_signal(int sig) return sig; } +static inline void target_sigemptyset(target_sigset_t *set) +{ + memset(set, 0, sizeof(*set)); +} + +static inline void target_sigaddset(target_sigset_t *set, int signum) +{ + signum--; + uint32_t mask = (uint32_t)1 << (signum % TARGET_NSIG_BPW); + set->__bits[signum / TARGET_NSIG_BPW] |= mask; +} + +static inline int target_sigismember(const target_sigset_t *set, int signum) +{ + signum--; + abi_ulong mask = (abi_ulong)1 << (signum % TARGET_NSIG_BPW); + return (set->__bits[signum / TARGET_NSIG_BPW] & mask) != 0; +} + /* Adjust the signal context to rewind out of safe-syscall if we're in it */ static inline void rewind_if_in_safe_syscall(void *puc) { @@ -60,6 +82,58 @@ static inline void rewind_if_in_safe_syscall(void *puc) } } +/* + * Note: The following take advantage of the BSD signal property that all + * signals are available on all architectures. + */ +static void host_to_target_sigset_internal(target_sigset_t *d, + const sigset_t *s) +{ + int i; + + target_sigemptyset(d); + for (i = 1; i <= NSIG; i++) { + if (sigismember(s, i)) { + target_sigaddset(d, host_to_target_signal(i)); + } + } +} + +void host_to_target_sigset(target_sigset_t *d, const sigset_t *s) +{ + target_sigset_t d1; + int i; + + host_to_target_sigset_internal(&d1, s); + for (i = 0; i < _SIG_WORDS; i++) { + d->__bits[i] = tswap32(d1.__bits[i]); + } +} + +static void target_to_host_sigset_internal(sigset_t *d, + const target_sigset_t *s) +{ + int i; + + sigemptyset(d); + for (i = 1; i <= TARGET_NSIG; i++) { + if (target_sigismember(s, i)) { + sigaddset(d, target_to_host_signal(i)); + } + } +} + +void target_to_host_sigset(sigset_t *d, const target_sigset_t *s) +{ + target_sigset_t s1; + int i; + + for (i = 0; i < TARGET_NSIG_WORDS; i++) { + s1.__bits[i] = tswap32(s->__bits[i]); + } + target_to_host_sigset_internal(d, &s1); +} + static bool has_trapno(int tsig) { return tsig == TARGET_SIGILL || From 46f4f76d332d8c2b4eb24c8e6f91ac8bdc205733 Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Sat, 8 Jan 2022 21:40:28 -0700 Subject: [PATCH 174/460] bsd-user/signal.c: setup_frame setup_frame sets up a signalled stack frame. Associated routines to extract the pointer to the stack frame and to support alternate stacks. Signed-off-by: Stacey Son Signed-off-by: Kyle Evans Signed-off-by: Warner Losh Reviewed-by: Richard Henderson --- bsd-user/main.c | 5 +++ bsd-user/qemu.h | 3 +- bsd-user/signal.c | 83 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 90 insertions(+), 1 deletion(-) diff --git a/bsd-user/main.c b/bsd-user/main.c index 29cf4e1569..f1d58e905e 100644 --- a/bsd-user/main.c +++ b/bsd-user/main.c @@ -217,6 +217,11 @@ void qemu_cpu_kick(CPUState *cpu) /* Assumes contents are already zeroed. */ static void init_task_state(TaskState *ts) { + ts->sigaltstack_used = (struct target_sigaltstack) { + .ss_sp = 0, + .ss_size = 0, + .ss_flags = TARGET_SS_DISABLE, + }; } void gemu_log(const char *fmt, ...) diff --git a/bsd-user/qemu.h b/bsd-user/qemu.h index 1648a509b9..de20650a00 100644 --- a/bsd-user/qemu.h +++ b/bsd-user/qemu.h @@ -107,7 +107,8 @@ typedef struct TaskState { */ sigset_t signal_mask; - uint8_t stack[]; + /* This thread's sigaltstack, if it has one */ + struct target_sigaltstack sigaltstack_used; } __attribute__((aligned(16))) TaskState; void stop_all_tasks(void); diff --git a/bsd-user/signal.c b/bsd-user/signal.c index 84dafa4e9f..dbc1373607 100644 --- a/bsd-user/signal.c +++ b/bsd-user/signal.c @@ -35,6 +35,16 @@ static void host_signal_handler(int host_sig, siginfo_t *info, void *puc); static void target_to_host_sigset_internal(sigset_t *d, const target_sigset_t *s); +static inline int on_sig_stack(TaskState *ts, unsigned long sp) +{ + return sp - ts->sigaltstack_used.ss_sp < ts->sigaltstack_used.ss_size; +} + +static inline int sas_ss_flags(TaskState *ts, unsigned long sp) +{ + return ts->sigaltstack_used.ss_size == 0 ? SS_DISABLE : + on_sig_stack(ts, sp) ? SS_ONSTACK : 0; +} /* * The BSD ABIs use the same singal numbers across all the CPU architectures, so @@ -491,6 +501,79 @@ static void host_signal_handler(int host_sig, siginfo_t *info, void *puc) cpu_exit(thread_cpu); } +static inline abi_ulong get_sigframe(struct target_sigaction *ka, + CPUArchState *env, size_t frame_size) +{ + TaskState *ts = (TaskState *)thread_cpu->opaque; + abi_ulong sp; + + /* Use default user stack */ + sp = get_sp_from_cpustate(env); + + if ((ka->sa_flags & TARGET_SA_ONSTACK) && sas_ss_flags(ts, sp) == 0) { + 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 +} + +/* compare to $M/$M/exec_machdep.c sendsig and sys/kern/kern_sig.c sigexit */ + +static void setup_frame(int sig, int code, struct target_sigaction *ka, + target_sigset_t *set, target_siginfo_t *tinfo, CPUArchState *env) +{ + struct target_sigframe *frame; + abi_ulong frame_addr; + int i; + + frame_addr = get_sigframe(ka, env, sizeof(*frame)); + trace_user_setup_frame(env, frame_addr); + if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { + unlock_user_struct(frame, frame_addr, 1); + dump_core_and_abort(TARGET_SIGILL); + return; + } + + memset(frame, 0, sizeof(*frame)); + setup_sigframe_arch(env, frame_addr, frame, 0); + + for (i = 0; i < TARGET_NSIG_WORDS; i++) { + __put_user(set->__bits[i], &frame->sf_uc.uc_sigmask.__bits[i]); + } + + if (tinfo) { + frame->sf_si.si_signo = tinfo->si_signo; + frame->sf_si.si_errno = tinfo->si_errno; + frame->sf_si.si_code = tinfo->si_code; + frame->sf_si.si_pid = tinfo->si_pid; + frame->sf_si.si_uid = tinfo->si_uid; + frame->sf_si.si_status = tinfo->si_status; + frame->sf_si.si_addr = tinfo->si_addr; + /* see host_to_target_siginfo_noswap() for more details */ + frame->sf_si.si_value.sival_ptr = tinfo->si_value.sival_ptr; + /* + * At this point, whatever is in the _reason union is complete + * and in target order, so just copy the whole thing over, even + * if it's too large for this specific signal. + * host_to_target_siginfo_noswap() and tswap_siginfo() have ensured + * that's so. + */ + memcpy(&frame->sf_si._reason, &tinfo->_reason, + sizeof(tinfo->_reason)); + } + + set_sigtramp_args(env, sig, frame, frame_addr, ka); + + unlock_user_struct(frame, frame_addr, 1); +} + void signal_init(void) { TaskState *ts = (TaskState *)thread_cpu->opaque; From 6c6d4b5616b391934851f049f41a7cbde12140d9 Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Sat, 8 Jan 2022 21:46:07 -0700 Subject: [PATCH 175/460] bsd-user/signal.c: handle_pending_signal Handle a queued signal. Signed-off-by: Stacey Son Signed-off-by: Kyle Evans Signed-off-by: Warner Losh Reviewed-by: Richard Henderson --- bsd-user/qemu.h | 7 ++++ bsd-user/signal.c | 87 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+) diff --git a/bsd-user/qemu.h b/bsd-user/qemu.h index de20650a00..02921ac8b3 100644 --- a/bsd-user/qemu.h +++ b/bsd-user/qemu.h @@ -99,6 +99,8 @@ typedef struct TaskState { * from multiple threads.) */ int signal_pending; + /* True if we're leaving a sigsuspend and sigsuspend_mask is valid. */ + bool in_sigsuspend; /* * This thread's signal mask, as requested by the guest program. * The actual signal mask of this thread may differ: @@ -106,6 +108,11 @@ typedef struct TaskState { * + sometimes we block all signals to avoid races */ sigset_t signal_mask; + /* + * The signal mask imposed by a guest sigsuspend syscall, if we are + * currently in the middle of such a syscall + */ + sigset_t sigsuspend_mask; /* This thread's sigaltstack, if it has one */ struct target_sigaltstack sigaltstack_used; diff --git a/bsd-user/signal.c b/bsd-user/signal.c index dbc1373607..366e047ccc 100644 --- a/bsd-user/signal.c +++ b/bsd-user/signal.c @@ -616,6 +616,93 @@ void signal_init(void) } } +static void handle_pending_signal(CPUArchState *env, int sig, + struct emulated_sigtable *k) +{ + CPUState *cpu = env_cpu(env); + TaskState *ts = cpu->opaque; + struct target_sigaction *sa; + int code; + sigset_t set; + abi_ulong handler; + target_siginfo_t tinfo; + target_sigset_t target_old_set; + + trace_user_handle_signal(env, sig); + + k->pending = 0; + + sig = gdb_handlesig(cpu, sig); + if (!sig) { + sa = NULL; + handler = TARGET_SIG_IGN; + } else { + sa = &sigact_table[sig - 1]; + handler = sa->_sa_handler; + } + + if (do_strace) { + print_taken_signal(sig, &k->info); + } + + if (handler == TARGET_SIG_DFL) { + /* + * default handler : ignore some signal. The other are job + * control or fatal. + */ + if (sig == TARGET_SIGTSTP || sig == TARGET_SIGTTIN || + sig == TARGET_SIGTTOU) { + kill(getpid(), SIGSTOP); + } else if (sig != TARGET_SIGCHLD && sig != TARGET_SIGURG && + sig != TARGET_SIGINFO && sig != TARGET_SIGWINCH && + sig != TARGET_SIGCONT) { + dump_core_and_abort(sig); + } + } else if (handler == TARGET_SIG_IGN) { + /* ignore sig */ + } else if (handler == TARGET_SIG_ERR) { + dump_core_and_abort(sig); + } else { + /* compute the blocked signals during the handler execution */ + sigset_t *blocked_set; + + target_to_host_sigset(&set, &sa->sa_mask); + /* + * SA_NODEFER indicates that the current signal should not be + * blocked during the handler. + */ + if (!(sa->sa_flags & TARGET_SA_NODEFER)) { + sigaddset(&set, target_to_host_signal(sig)); + } + + /* + * Save the previous blocked signal state to restore it at the + * end of the signal execution (see do_sigreturn). + */ + host_to_target_sigset_internal(&target_old_set, &ts->signal_mask); + + blocked_set = ts->in_sigsuspend ? + &ts->sigsuspend_mask : &ts->signal_mask; + sigorset(&ts->signal_mask, blocked_set, &set); + ts->in_sigsuspend = false; + sigprocmask(SIG_SETMASK, &ts->signal_mask, NULL); + + /* XXX VM86 on x86 ??? */ + + code = k->info.si_code; /* From host, so no si_type */ + /* prepare the stack frame of the virtual CPU */ + if (sa->sa_flags & TARGET_SA_SIGINFO) { + tswap_siginfo(&tinfo, &k->info); + setup_frame(sig, code, sa, &target_old_set, &tinfo, env); + } else { + setup_frame(sig, code, sa, &target_old_set, NULL, env); + } + if (sa->sa_flags & TARGET_SA_RESETHAND) { + sa->_sa_handler = TARGET_SIG_DFL; + } + } +} + void process_pending_signals(CPUArchState *cpu_env) { } From 08eb66d5d837c2db9d5d57553da8448fd8e36571 Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Sat, 8 Jan 2022 21:48:25 -0700 Subject: [PATCH 176/460] bsd-user/signal.c: tswap_siginfo Convert siginfo from targer to host. Signed-off-by: Stacey Son Signed-off-by: Kyle Evans Signed-off-by: Warner Losh Reviewed-by: Richard Henderson --- bsd-user/signal.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/bsd-user/signal.c b/bsd-user/signal.c index 366e047ccc..34e8c811ad 100644 --- a/bsd-user/signal.c +++ b/bsd-user/signal.c @@ -256,6 +256,59 @@ static inline void host_to_target_siginfo_noswap(target_siginfo_t *tinfo, tinfo->si_code = deposit32(si_code, 24, 8, si_type); } +static void tswap_siginfo(target_siginfo_t *tinfo, const target_siginfo_t *info) +{ + int si_type = extract32(info->si_code, 24, 8); + int si_code = sextract32(info->si_code, 0, 24); + + __put_user(info->si_signo, &tinfo->si_signo); + __put_user(info->si_errno, &tinfo->si_errno); + __put_user(si_code, &tinfo->si_code); /* Zero out si_type, it's internal */ + __put_user(info->si_pid, &tinfo->si_pid); + __put_user(info->si_uid, &tinfo->si_uid); + __put_user(info->si_status, &tinfo->si_status); + __put_user(info->si_addr, &tinfo->si_addr); + /* + * Unswapped, because we passed it through mostly untouched. si_value is + * opaque to the kernel, so we didn't bother with potentially wasting cycles + * to swap it into host byte order. + */ + tinfo->si_value.sival_ptr = info->si_value.sival_ptr; + + /* + * We can use our internal marker of which fields in the structure + * are valid, rather than duplicating the guesswork of + * host_to_target_siginfo_noswap() here. + */ + switch (si_type) { + case QEMU_SI_NOINFO: /* No additional info */ + break; + case QEMU_SI_FAULT: + __put_user(info->_reason._fault._trapno, + &tinfo->_reason._fault._trapno); + break; + case QEMU_SI_TIMER: + __put_user(info->_reason._timer._timerid, + &tinfo->_reason._timer._timerid); + __put_user(info->_reason._timer._overrun, + &tinfo->_reason._timer._overrun); + break; + case QEMU_SI_MESGQ: + __put_user(info->_reason._mesgq._mqd, &tinfo->_reason._mesgq._mqd); + break; + case QEMU_SI_POLL: + /* Note: Not generated on FreeBSD */ + __put_user(info->_reason._poll._band, &tinfo->_reason._poll._band); + break; + case QEMU_SI_CAPSICUM: + __put_user(info->_reason._capsicum._syscall, + &tinfo->_reason._capsicum._syscall); + break; + default: + g_assert_not_reached(); + } +} + /* Returns 1 if given signal should dump core if not handled. */ static int core_dump_signal(int sig) { From d7acd31780bea1c192854a8617255ad992b19c4d Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Sat, 8 Jan 2022 21:52:26 -0700 Subject: [PATCH 177/460] bsd-user/signal.c: process_pending_signals Process the currently queued signals. Signed-off-by: Stacey Son Signed-off-by: Kyle Evans Signed-off-by: Warner Losh Reviewed-by: Richard Henderson --- bsd-user/signal.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/bsd-user/signal.c b/bsd-user/signal.c index 34e8c811ad..4b398745f4 100644 --- a/bsd-user/signal.c +++ b/bsd-user/signal.c @@ -756,8 +756,62 @@ static void handle_pending_signal(CPUArchState *env, int sig, } } -void process_pending_signals(CPUArchState *cpu_env) +void process_pending_signals(CPUArchState *env) { + CPUState *cpu = env_cpu(env); + int sig; + sigset_t *blocked_set, set; + struct emulated_sigtable *k; + TaskState *ts = cpu->opaque; + + while (qatomic_read(&ts->signal_pending)) { + sigfillset(&set); + sigprocmask(SIG_SETMASK, &set, 0); + + restart_scan: + sig = ts->sync_signal.pending; + if (sig) { + /* + * Synchronous signals are forced by the emulated CPU in some way. + * If they are set to ignore, restore the default handler (see + * sys/kern_sig.c trapsignal() and execsigs() for this behavior) + * though maybe this is done only when forcing exit for non SIGCHLD. + */ + if (sigismember(&ts->signal_mask, target_to_host_signal(sig)) || + sigact_table[sig - 1]._sa_handler == TARGET_SIG_IGN) { + sigdelset(&ts->signal_mask, target_to_host_signal(sig)); + sigact_table[sig - 1]._sa_handler = TARGET_SIG_DFL; + } + handle_pending_signal(env, sig, &ts->sync_signal); + } + + k = ts->sigtab; + for (sig = 1; sig <= TARGET_NSIG; sig++, k++) { + blocked_set = ts->in_sigsuspend ? + &ts->sigsuspend_mask : &ts->signal_mask; + if (k->pending && + !sigismember(blocked_set, target_to_host_signal(sig))) { + handle_pending_signal(env, sig, k); + /* + * Restart scan from the beginning, as handle_pending_signal + * might have resulted in a new synchronous signal (eg SIGSEGV). + */ + goto restart_scan; + } + } + + /* + * Unblock signals and check one more time. Unblocking signals may cause + * us to take another host signal, which will set signal_pending again. + */ + qatomic_set(&ts->signal_pending, 0); + ts->in_sigsuspend = false; + set = ts->signal_mask; + sigdelset(&set, SIGSEGV); + sigdelset(&set, SIGBUS); + sigprocmask(SIG_SETMASK, &set, 0); + } + ts->in_sigsuspend = false; } void cpu_loop_exit_sigsegv(CPUState *cpu, target_ulong addr, From 6384dd534d742123d26c008d9794b20bc41359d5 Mon Sep 17 00:00:00 2001 From: Hanna Reitz Date: Tue, 18 Jan 2022 18:00:00 +0100 Subject: [PATCH 178/460] iotests/block-status-cache: New test Add a new test to verify that want_zero=false block-status calls do not pollute the block-status cache for want_zero=true calls. We check want_zero=true calls and their results using `qemu-img map` (over NBD), and want_zero=false calls also using `qemu-img map` over NBD, but using the qemu:allocation-depth context. (This test case cannot be integrated into nbd-qemu-allocation, because that is a qcow2 test, and this is a raw test.) Signed-off-by: Hanna Reitz Message-Id: <20220118170000.49423-3-hreitz@redhat.com> Reviewed-by: Nir Soffer Reviewed-by: Eric Blake Tested-by: Eric Blake Signed-off-by: Eric Blake --- tests/qemu-iotests/tests/block-status-cache | 139 ++++++++++++++++++ .../qemu-iotests/tests/block-status-cache.out | 5 + 2 files changed, 144 insertions(+) create mode 100755 tests/qemu-iotests/tests/block-status-cache create mode 100644 tests/qemu-iotests/tests/block-status-cache.out diff --git a/tests/qemu-iotests/tests/block-status-cache b/tests/qemu-iotests/tests/block-status-cache new file mode 100755 index 0000000000..6fa10bb8f8 --- /dev/null +++ b/tests/qemu-iotests/tests/block-status-cache @@ -0,0 +1,139 @@ +#!/usr/bin/env python3 +# group: rw quick +# +# Test cases for the block-status cache. +# +# Copyright (C) 2022 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 . +# + +import os +import signal +import iotests +from iotests import qemu_img_create, qemu_img_pipe, qemu_nbd + + +image_size = 1 * 1024 * 1024 +test_img = os.path.join(iotests.test_dir, 'test.img') + +nbd_pidfile = os.path.join(iotests.test_dir, 'nbd.pid') +nbd_sock = os.path.join(iotests.sock_dir, 'nbd.sock') + + +class TestBscWithNbd(iotests.QMPTestCase): + def setUp(self) -> None: + """Just create an empty image with a read-only NBD server on it""" + assert qemu_img_create('-f', iotests.imgfmt, test_img, + str(image_size)) == 0 + + # Pass --allocation-depth to enable the qemu:allocation-depth context, + # which we are going to query to provoke a block-status inquiry with + # want_zero=false. + assert qemu_nbd(f'--socket={nbd_sock}', + f'--format={iotests.imgfmt}', + '--persistent', + '--allocation-depth', + '--read-only', + f'--pid-file={nbd_pidfile}', + test_img) \ + == 0 + + def tearDown(self) -> None: + with open(nbd_pidfile, encoding='utf-8') as f: + pid = int(f.read()) + os.kill(pid, signal.SIGTERM) + os.remove(nbd_pidfile) + os.remove(test_img) + + def test_with_zero_bug(self) -> None: + """ + Verify that the block-status cache is not corrupted by a + want_zero=false call. + We can provoke a want_zero=false call with `qemu-img map` over NBD with + x-dirty-bitmap=qemu:allocation-depth, so we first run a normal `map` + (which results in want_zero=true), then using said + qemu:allocation-depth context, and finally another normal `map` to + verify that the cache has not been corrupted. + """ + + nbd_img_opts = f'driver=nbd,server.type=unix,server.path={nbd_sock}' + nbd_img_opts_alloc_depth = nbd_img_opts + \ + ',x-dirty-bitmap=qemu:allocation-depth' + + # Normal map, results in want_zero=true. + # This will probably detect an allocated data sector first (qemu likes + # to allocate the first sector to facilitate alignment probing), and + # then the rest to be zero. The BSC will thus contain (if anything) + # one range covering the first sector. + map_pre = qemu_img_pipe('map', '--output=json', '--image-opts', + nbd_img_opts) + + # qemu:allocation-depth maps for want_zero=false. + # want_zero=false should (with the file driver, which the server is + # using) report everything as data. While this is sufficient for + # want_zero=false, this is nothing that should end up in the + # block-status cache. + # Due to a bug, this information did end up in the cache, though, and + # this would lead to wrong information being returned on subsequent + # want_zero=true calls. + # + # We need to run this map twice: On the first call, we probably still + # have the first sector in the cache, and so this will be served from + # the cache; and only the subsequent range will be queried from the + # block driver. This subsequent range will then be entered into the + # cache. + # If we did a want_zero=true call at this point, we would thus get + # correct information: The first sector is not covered by the cache, so + # we would get fresh block-status information from the driver, which + # would return a data range, and this would then go into the cache, + # evicting the wrong range from the want_zero=false call before. + # + # Therefore, we need a second want_zero=false map to reproduce: + # Since the first sector is not in the cache, the query for its status + # will go to the driver, which will return a result that reports the + # whole image to be a single data area. This result will then go into + # the cache, and so the cache will then report the whole image to + # contain data. + # + # Note that once the cache reports the whole image to contain data, any + # subsequent map operation will be served from the cache, and so we can + # never loop too many times here. + for _ in range(2): + # (Ignore the result, this is just to contaminate the cache) + qemu_img_pipe('map', '--output=json', '--image-opts', + nbd_img_opts_alloc_depth) + + # Now let's see whether the cache reports everything as data, or + # whether we get correct information (i.e. the same as we got on our + # first attempt). + map_post = qemu_img_pipe('map', '--output=json', '--image-opts', + nbd_img_opts) + + if map_pre != map_post: + print('ERROR: Map information differs before and after querying ' + + 'qemu:allocation-depth') + print('Before:') + print(map_pre) + print('After:') + print(map_post) + + self.fail("Map information differs") + + +if __name__ == '__main__': + # The block-status cache only works on the protocol layer, so to test it, + # we can only use the raw format + iotests.main(supported_fmts=['raw'], + supported_protocols=['file']) diff --git a/tests/qemu-iotests/tests/block-status-cache.out b/tests/qemu-iotests/tests/block-status-cache.out new file mode 100644 index 0000000000..ae1213e6f8 --- /dev/null +++ b/tests/qemu-iotests/tests/block-status-cache.out @@ -0,0 +1,5 @@ +. +---------------------------------------------------------------------- +Ran 1 tests + +OK From c885ae0e4ebf207c861bf651dcf9282677281c06 Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Sat, 8 Jan 2022 23:48:12 -0700 Subject: [PATCH 179/460] bsd-user/signal.c: implement do_sigreturn Implements the meat of a sigreturn(2) system call via do_sigreturn, and helper reset_signal_mask. Fix the prototype of do_sigreturn in qemu.h and remove do_rt_sigreturn since it's linux only. Signed-off-by: Stacey Son Signed-off-by: Kyle Evans Signed-off-by: Warner Losh Reviewed-by: Richard Henderson --- bsd-user/signal-common.h | 2 +- bsd-user/signal.c | 56 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 1 deletion(-) diff --git a/bsd-user/signal-common.h b/bsd-user/signal-common.h index ee819266f5..786ec592d1 100644 --- a/bsd-user/signal-common.h +++ b/bsd-user/signal-common.h @@ -11,7 +11,7 @@ long do_rt_sigreturn(CPUArchState *env); abi_long do_sigaltstack(abi_ulong uss_addr, abi_ulong uoss_addr, abi_ulong sp); -long do_sigreturn(CPUArchState *env); +long do_sigreturn(CPUArchState *env, abi_ulong addr); void force_sig_fault(int sig, int code, abi_ulong addr); int host_to_target_signal(int sig); void host_to_target_sigset(target_sigset_t *d, const sigset_t *s); diff --git a/bsd-user/signal.c b/bsd-user/signal.c index 4b398745f4..150262a87e 100644 --- a/bsd-user/signal.c +++ b/bsd-user/signal.c @@ -627,6 +627,62 @@ static void setup_frame(int sig, int code, struct target_sigaction *ka, unlock_user_struct(frame, frame_addr, 1); } +static int reset_signal_mask(target_ucontext_t *ucontext) +{ + int i; + sigset_t blocked; + target_sigset_t target_set; + TaskState *ts = (TaskState *)thread_cpu->opaque; + + for (i = 0; i < TARGET_NSIG_WORDS; i++) { + if (__get_user(target_set.__bits[i], + &ucontext->uc_sigmask.__bits[i])) { + return -TARGET_EFAULT; + } + } + target_to_host_sigset_internal(&blocked, &target_set); + ts->signal_mask = blocked; + + return 0; +} + +/* See sys/$M/$M/exec_machdep.c sigreturn() */ +long do_sigreturn(CPUArchState *env, abi_ulong addr) +{ + long ret; + abi_ulong target_ucontext; + target_ucontext_t *ucontext = NULL; + + /* Get the target ucontext address from the stack frame */ + ret = get_ucontext_sigreturn(env, addr, &target_ucontext); + if (is_error(ret)) { + return ret; + } + trace_user_do_sigreturn(env, addr); + if (!lock_user_struct(VERIFY_READ, ucontext, target_ucontext, 0)) { + goto badframe; + } + + /* Set the register state back to before the signal. */ + if (set_mcontext(env, &ucontext->uc_mcontext, 1)) { + goto badframe; + } + + /* And reset the signal mask. */ + if (reset_signal_mask(ucontext)) { + goto badframe; + } + + unlock_user_struct(ucontext, target_ucontext, 0); + return -TARGET_EJUSTRETURN; + +badframe: + if (ucontext != NULL) { + unlock_user_struct(ucontext, target_ucontext, 0); + } + return -TARGET_EFAULT; +} + void signal_init(void) { TaskState *ts = (TaskState *)thread_cpu->opaque; From 394cf694273caf8ab8838588954d0fc2909ae2fa Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Sat, 8 Jan 2022 23:59:42 -0700 Subject: [PATCH 180/460] bsd-user/signal.c: implement do_sigaction Implement the meat of the sigaction(2) system call with do_sigaction and helper routiner block_signals (which is also used to implemement signal masking so it's global). Signed-off-by: Stacey Son Signed-off-by: Kyle Evans Signed-off-by: Warner Losh Reviewed-by: Richard Henderson --- bsd-user/signal-common.h | 22 +++++++++++ bsd-user/signal.c | 82 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 104 insertions(+) diff --git a/bsd-user/signal-common.h b/bsd-user/signal-common.h index 786ec592d1..7ff8e8f2e4 100644 --- a/bsd-user/signal-common.h +++ b/bsd-user/signal-common.h @@ -9,7 +9,29 @@ #ifndef SIGNAL_COMMON_H #define SIGNAL_COMMON_H +/** + * block_signals: block all signals while handling this guest syscall + * + * Block all signals, and arrange that the signal mask is returned to + * its correct value for the guest before we resume execution of guest code. + * If this function returns non-zero, then the caller should immediately + * return -TARGET_ERESTARTSYS to the main loop, which will take the pending + * signal and restart execution of the syscall. + * If block_signals() returns zero, then the caller can continue with + * emulation of the system call knowing that no signals can be taken + * (and therefore that no race conditions will result). + * This should only be called once, because if it is called a second time + * it will always return non-zero. (Think of it like a mutex that can't + * be recursively locked.) + * Signals will be unblocked again by process_pending_signals(). + * + * Return value: non-zero if there was a pending signal, zero if not. + */ +int block_signals(void); /* Returns non zero if signal pending */ + long do_rt_sigreturn(CPUArchState *env); +int do_sigaction(int sig, const struct target_sigaction *act, + struct target_sigaction *oact); abi_long do_sigaltstack(abi_ulong uss_addr, abi_ulong uoss_addr, abi_ulong sp); long do_sigreturn(CPUArchState *env, abi_ulong addr); void force_sig_fault(int sig, int code, abi_ulong addr); diff --git a/bsd-user/signal.c b/bsd-user/signal.c index 150262a87e..5c94bd02e3 100644 --- a/bsd-user/signal.c +++ b/bsd-user/signal.c @@ -309,6 +309,25 @@ static void tswap_siginfo(target_siginfo_t *tinfo, const target_siginfo_t *info) } } +int block_signals(void) +{ + TaskState *ts = (TaskState *)thread_cpu->opaque; + sigset_t set; + + /* + * It's OK to block everything including SIGSEGV, because we won't run any + * further guest code before unblocking signals in + * process_pending_signals(). We depend on the FreeBSD behaivor here where + * this will only affect this thread's signal mask. We don't use + * pthread_sigmask which might seem more correct because that routine also + * does odd things with SIGCANCEL to implement pthread_cancel(). + */ + sigfillset(&set); + sigprocmask(SIG_SETMASK, &set, 0); + + return qatomic_xchg(&ts->signal_pending, 1); +} + /* Returns 1 if given signal should dump core if not handled. */ static int core_dump_signal(int sig) { @@ -554,6 +573,69 @@ static void host_signal_handler(int host_sig, siginfo_t *info, void *puc) cpu_exit(thread_cpu); } +/* do_sigaction() return host values and errnos */ +int do_sigaction(int sig, const struct target_sigaction *act, + struct target_sigaction *oact) +{ + struct target_sigaction *k; + struct sigaction act1; + int host_sig; + int ret = 0; + + if (sig < 1 || sig > TARGET_NSIG) { + return -TARGET_EINVAL; + } + + if ((sig == TARGET_SIGKILL || sig == TARGET_SIGSTOP) && + act != NULL && act->_sa_handler != TARGET_SIG_DFL) { + return -TARGET_EINVAL; + } + + if (block_signals()) { + return -TARGET_ERESTART; + } + + k = &sigact_table[sig - 1]; + if (oact) { + oact->_sa_handler = tswapal(k->_sa_handler); + oact->sa_flags = tswap32(k->sa_flags); + oact->sa_mask = k->sa_mask; + } + if (act) { + k->_sa_handler = tswapal(act->_sa_handler); + k->sa_flags = tswap32(act->sa_flags); + k->sa_mask = act->sa_mask; + + /* Update the host signal state. */ + host_sig = target_to_host_signal(sig); + if (host_sig != SIGSEGV && host_sig != SIGBUS) { + memset(&act1, 0, sizeof(struct sigaction)); + sigfillset(&act1.sa_mask); + act1.sa_flags = SA_SIGINFO; + if (k->sa_flags & TARGET_SA_RESTART) { + act1.sa_flags |= SA_RESTART; + } + /* + * Note: It is important to update the host kernel signal mask to + * avoid getting unexpected interrupted system calls. + */ + if (k->_sa_handler == TARGET_SIG_IGN) { + act1.sa_sigaction = (void *)SIG_IGN; + } else if (k->_sa_handler == TARGET_SIG_DFL) { + if (fatal_signal(sig)) { + act1.sa_sigaction = host_signal_handler; + } else { + act1.sa_sigaction = (void *)SIG_DFL; + } + } else { + act1.sa_sigaction = host_signal_handler; + } + ret = sigaction(host_sig, &act1, NULL); + } + } + return ret; +} + static inline abi_ulong get_sigframe(struct target_sigaction *ka, CPUArchState *env, size_t frame_size) { From 43ed4267845899871890589c96b1302a1696525d Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Sun, 9 Jan 2022 00:06:56 -0700 Subject: [PATCH 181/460] bsd-user/signal.c: do_sigaltstack Implement the meat of the sigaltstack(2) system call with do_sigaltstack. With that, all the stubbed out routines are complete, so remove now-incorrect comment. Signed-off-by: Stacey Son Signed-off-by: Kyle Evans Signed-off-by: Warner Losh Reviewed-by: Richard Henderson --- bsd-user/signal.c | 72 +++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 67 insertions(+), 5 deletions(-) diff --git a/bsd-user/signal.c b/bsd-user/signal.c index 5c94bd02e3..ad22ba9d90 100644 --- a/bsd-user/signal.c +++ b/bsd-user/signal.c @@ -25,11 +25,6 @@ #include "hw/core/tcg-cpu-ops.h" #include "host-signal.h" -/* - * Stubbed out routines until we merge signal support from bsd-user - * fork. - */ - static struct target_sigaction sigact_table[TARGET_NSIG]; static void host_signal_handler(int host_sig, siginfo_t *info, void *puc); static void target_to_host_sigset_internal(sigset_t *d, @@ -573,6 +568,73 @@ static void host_signal_handler(int host_sig, siginfo_t *info, void *puc) cpu_exit(thread_cpu); } +/* do_sigaltstack() returns target values and errnos. */ +/* compare to kern/kern_sig.c sys_sigaltstack() and kern_sigaltstack() */ +abi_long do_sigaltstack(abi_ulong uss_addr, abi_ulong uoss_addr, abi_ulong sp) +{ + TaskState *ts = (TaskState *)thread_cpu->opaque; + int ret; + target_stack_t oss; + + if (uoss_addr) { + /* Save current signal stack params */ + oss.ss_sp = tswapl(ts->sigaltstack_used.ss_sp); + oss.ss_size = tswapl(ts->sigaltstack_used.ss_size); + oss.ss_flags = tswapl(sas_ss_flags(ts, sp)); + } + + if (uss_addr) { + target_stack_t *uss; + target_stack_t ss; + size_t minstacksize = TARGET_MINSIGSTKSZ; + + ret = -TARGET_EFAULT; + if (!lock_user_struct(VERIFY_READ, uss, uss_addr, 1)) { + goto out; + } + __get_user(ss.ss_sp, &uss->ss_sp); + __get_user(ss.ss_size, &uss->ss_size); + __get_user(ss.ss_flags, &uss->ss_flags); + unlock_user_struct(uss, uss_addr, 0); + + ret = -TARGET_EPERM; + if (on_sig_stack(ts, sp)) { + goto out; + } + + ret = -TARGET_EINVAL; + if (ss.ss_flags != TARGET_SS_DISABLE + && ss.ss_flags != TARGET_SS_ONSTACK + && ss.ss_flags != 0) { + goto out; + } + + if (ss.ss_flags == TARGET_SS_DISABLE) { + ss.ss_size = 0; + ss.ss_sp = 0; + } else { + ret = -TARGET_ENOMEM; + if (ss.ss_size < minstacksize) { + goto out; + } + } + + ts->sigaltstack_used.ss_sp = ss.ss_sp; + ts->sigaltstack_used.ss_size = ss.ss_size; + } + + if (uoss_addr) { + ret = -TARGET_EFAULT; + if (copy_to_user(uoss_addr, &oss, sizeof(oss))) { + goto out; + } + } + + ret = 0; +out: + return ret; +} + /* do_sigaction() return host values and errnos */ int do_sigaction(int sig, const struct target_sigaction *act, struct target_sigaction *oact) From adbae40fa1c378d68b2d329620d16bbb8e222eb7 Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Sun, 9 Jan 2022 11:50:24 -0700 Subject: [PATCH 182/460] MAINTAINERS: Add tests/vm/*bsd to the list to get reviews on tests/vm/*bsd (especailly tests/vm/freebsd) are adjacent to the bsd-user stuff and we're keen on keeping them working as well. Signed-off-by: Warner Losh Reviewed-by: Richard Henderson --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index e4b3a4bcdf..b7487f9b54 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3181,6 +3181,7 @@ R: Kyle Evans S: Maintained F: bsd-user/ F: configs/targets/*-bsd-user.mak +F: tests/vm/*bsd T: git https://github.com/qemu-bsd-user/qemu-bsd-user bsd-user-rebase-3.1 Linux user From bab6ccc53d4dcfc7a713efbcce9ec60ada9e29b9 Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Mon, 24 Jan 2022 15:14:04 -0700 Subject: [PATCH 183/460] bsd-user: Rename arg name for target_cpu_reset to env Rename the parameter name for target_cpu_reset's CPUArchState * arg from cpu to env. Signed-off-by: Warner Losh Reviewed-by: Richard Henderson --- bsd-user/arm/target_arch_cpu.h | 2 +- bsd-user/i386/target_arch_cpu.h | 4 ++-- bsd-user/x86_64/target_arch_cpu.h | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/bsd-user/arm/target_arch_cpu.h b/bsd-user/arm/target_arch_cpu.h index 2b395d5c97..b087db48fa 100644 --- a/bsd-user/arm/target_arch_cpu.h +++ b/bsd-user/arm/target_arch_cpu.h @@ -213,7 +213,7 @@ static inline void target_cpu_clone_regs(CPUARMState *env, target_ulong newsp) env->regs[0] = 0; } -static inline void target_cpu_reset(CPUArchState *cpu) +static inline void target_cpu_reset(CPUArchState *env) { } diff --git a/bsd-user/i386/target_arch_cpu.h b/bsd-user/i386/target_arch_cpu.h index 472a96689f..3cbf69d8af 100644 --- a/bsd-user/i386/target_arch_cpu.h +++ b/bsd-user/i386/target_arch_cpu.h @@ -200,9 +200,9 @@ static inline void target_cpu_clone_regs(CPUX86State *env, target_ulong newsp) env->regs[R_EAX] = 0; } -static inline void target_cpu_reset(CPUArchState *cpu) +static inline void target_cpu_reset(CPUArchState *env) { - cpu_reset(env_cpu(cpu)); + cpu_reset(env_cpu(env)); } #endif /* ! _TARGET_ARCH_CPU_H_ */ diff --git a/bsd-user/x86_64/target_arch_cpu.h b/bsd-user/x86_64/target_arch_cpu.h index 14def48adb..0a9c0f0894 100644 --- a/bsd-user/x86_64/target_arch_cpu.h +++ b/bsd-user/x86_64/target_arch_cpu.h @@ -238,9 +238,9 @@ static inline void target_cpu_clone_regs(CPUX86State *env, target_ulong newsp) env->regs[R_EAX] = 0; } -static inline void target_cpu_reset(CPUArchState *cpu) +static inline void target_cpu_reset(CPUArchState *env) { - cpu_reset(env_cpu(cpu)); + cpu_reset(env_cpu(env)); } #endif /* ! _TARGET_ARCH_CPU_H_ */ From 1103d59caaa82c94b4223a5429c31895d2f05217 Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Mon, 24 Jan 2022 15:23:13 -0700 Subject: [PATCH 184/460] bsd-user/freebsd/target_os_ucontext.h: Prefer env as arg name for CPUArchState args Signed-off-by: Warner Losh Reviewed-by: Richard Henderson --- bsd-user/freebsd/target_os_ucontext.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bsd-user/freebsd/target_os_ucontext.h b/bsd-user/freebsd/target_os_ucontext.h index 41b28b2c15..b196b1c629 100644 --- a/bsd-user/freebsd/target_os_ucontext.h +++ b/bsd-user/freebsd/target_os_ucontext.h @@ -36,9 +36,9 @@ abi_long set_sigtramp_args(CPUArchState *env, int sig, struct target_sigframe *frame, abi_ulong frame_addr, struct target_sigaction *ka); -abi_long get_mcontext(CPUArchState *regs, target_mcontext_t *mcp, int flags); -abi_long set_mcontext(CPUArchState *regs, target_mcontext_t *mcp, int srflag); -abi_long get_ucontext_sigreturn(CPUArchState *regs, abi_ulong target_sf, +abi_long get_mcontext(CPUArchState *env, target_mcontext_t *mcp, int flags); +abi_long set_mcontext(CPUArchState *env, target_mcontext_t *mcp, int srflag); +abi_long get_ucontext_sigreturn(CPUArchState *env, abi_ulong target_sf, abi_ulong *target_uc); #endif /* TARGET_OS_UCONTEXT_H */ From b13e49bc86961c6725b2ebddd53898fe1366f6dc Mon Sep 17 00:00:00 2001 From: Serge Belyshev Date: Sat, 29 Jan 2022 22:46:59 +0300 Subject: [PATCH 185/460] linux-user: Move generic TARGET_RLIMIT* definitions to generic/target_resource.h Signed-off-by: Serge Belyshev Message-Id: <87ee4ql3yk.fsf_-_@depni.sinp.msu.ru> Signed-off-by: Laurent Vivier --- linux-user/aarch64/target_resource.h | 1 + linux-user/alpha/target_resource.h | 21 ++++++++++ linux-user/arm/target_resource.h | 1 + linux-user/cris/target_resource.h | 1 + linux-user/generic/target_resource.h | 37 +++++++++++++++++ linux-user/hexagon/target_resource.h | 1 + linux-user/hppa/target_resource.h | 1 + linux-user/i386/target_resource.h | 1 + linux-user/m68k/target_resource.h | 1 + linux-user/microblaze/target_resource.h | 1 + linux-user/mips/target_resource.h | 24 +++++++++++ linux-user/mips64/target_resource.h | 1 + linux-user/nios2/target_resource.h | 1 + linux-user/openrisc/target_resource.h | 1 + linux-user/ppc/target_resource.h | 1 + linux-user/riscv/target_resource.h | 1 + linux-user/s390x/target_resource.h | 1 + linux-user/sh4/target_resource.h | 1 + linux-user/sparc/target_resource.h | 17 ++++++++ linux-user/syscall_defs.h | 53 +------------------------ linux-user/x86_64/target_resource.h | 1 + linux-user/xtensa/target_resource.h | 1 + 22 files changed, 117 insertions(+), 52 deletions(-) create mode 100644 linux-user/aarch64/target_resource.h create mode 100644 linux-user/alpha/target_resource.h create mode 100644 linux-user/arm/target_resource.h create mode 100644 linux-user/cris/target_resource.h create mode 100644 linux-user/generic/target_resource.h create mode 100644 linux-user/hexagon/target_resource.h create mode 100644 linux-user/hppa/target_resource.h create mode 100644 linux-user/i386/target_resource.h create mode 100644 linux-user/m68k/target_resource.h create mode 100644 linux-user/microblaze/target_resource.h create mode 100644 linux-user/mips/target_resource.h create mode 100644 linux-user/mips64/target_resource.h create mode 100644 linux-user/nios2/target_resource.h create mode 100644 linux-user/openrisc/target_resource.h create mode 100644 linux-user/ppc/target_resource.h create mode 100644 linux-user/riscv/target_resource.h create mode 100644 linux-user/s390x/target_resource.h create mode 100644 linux-user/sh4/target_resource.h create mode 100644 linux-user/sparc/target_resource.h create mode 100644 linux-user/x86_64/target_resource.h create mode 100644 linux-user/xtensa/target_resource.h diff --git a/linux-user/aarch64/target_resource.h b/linux-user/aarch64/target_resource.h new file mode 100644 index 0000000000..227259594c --- /dev/null +++ b/linux-user/aarch64/target_resource.h @@ -0,0 +1 @@ +#include "../generic/target_resource.h" diff --git a/linux-user/alpha/target_resource.h b/linux-user/alpha/target_resource.h new file mode 100644 index 0000000000..c9b082faee --- /dev/null +++ b/linux-user/alpha/target_resource.h @@ -0,0 +1,21 @@ +#ifndef ALPHA_TARGET_RESOURCE_H +#define ALPHA_TARGET_RESOURCE_H + +#include "../generic/target_resource.h" + +#undef TARGET_RLIM_INFINITY +#define TARGET_RLIM_INFINITY 0x7fffffffffffffffull + +#undef TARGET_RLIMIT_NOFILE +#define TARGET_RLIMIT_NOFILE 6 + +#undef TARGET_RLIMIT_AS +#define TARGET_RLIMIT_AS 7 + +#undef TARGET_RLIMIT_NPROC +#define TARGET_RLIMIT_NPROC 8 + +#undef TARGET_RLIMIT_MEMLOCK +#define TARGET_RLIMIT_MEMLOCK 9 + +#endif diff --git a/linux-user/arm/target_resource.h b/linux-user/arm/target_resource.h new file mode 100644 index 0000000000..227259594c --- /dev/null +++ b/linux-user/arm/target_resource.h @@ -0,0 +1 @@ +#include "../generic/target_resource.h" diff --git a/linux-user/cris/target_resource.h b/linux-user/cris/target_resource.h new file mode 100644 index 0000000000..227259594c --- /dev/null +++ b/linux-user/cris/target_resource.h @@ -0,0 +1 @@ +#include "../generic/target_resource.h" diff --git a/linux-user/generic/target_resource.h b/linux-user/generic/target_resource.h new file mode 100644 index 0000000000..f04c93b125 --- /dev/null +++ b/linux-user/generic/target_resource.h @@ -0,0 +1,37 @@ +/* + * Target definitions of RLIMIT_* constants. These may be overridden by an + * architecture specific header if needed. + */ + +#ifndef GENERIC_TARGET_RESOURCE_H +#define GENERIC_TARGET_RESOURCE_H + +struct target_rlimit { + abi_ulong rlim_cur; + abi_ulong rlim_max; +}; + +struct target_rlimit64 { + uint64_t rlim_cur; + uint64_t rlim_max; +}; + +#define TARGET_RLIM_INFINITY ((abi_ulong)-1) + +#define TARGET_RLIMIT_CPU 0 +#define TARGET_RLIMIT_FSIZE 1 +#define TARGET_RLIMIT_DATA 2 +#define TARGET_RLIMIT_STACK 3 +#define TARGET_RLIMIT_CORE 4 +#define TARGET_RLIMIT_RSS 5 +#define TARGET_RLIMIT_NPROC 6 +#define TARGET_RLIMIT_NOFILE 7 +#define TARGET_RLIMIT_MEMLOCK 8 +#define TARGET_RLIMIT_AS 9 +#define TARGET_RLIMIT_LOCKS 10 +#define TARGET_RLIMIT_SIGPENDING 11 +#define TARGET_RLIMIT_MSGQUEUE 12 +#define TARGET_RLIMIT_NICE 13 +#define TARGET_RLIMIT_RTPRIO 14 + +#endif diff --git a/linux-user/hexagon/target_resource.h b/linux-user/hexagon/target_resource.h new file mode 100644 index 0000000000..227259594c --- /dev/null +++ b/linux-user/hexagon/target_resource.h @@ -0,0 +1 @@ +#include "../generic/target_resource.h" diff --git a/linux-user/hppa/target_resource.h b/linux-user/hppa/target_resource.h new file mode 100644 index 0000000000..227259594c --- /dev/null +++ b/linux-user/hppa/target_resource.h @@ -0,0 +1 @@ +#include "../generic/target_resource.h" diff --git a/linux-user/i386/target_resource.h b/linux-user/i386/target_resource.h new file mode 100644 index 0000000000..227259594c --- /dev/null +++ b/linux-user/i386/target_resource.h @@ -0,0 +1 @@ +#include "../generic/target_resource.h" diff --git a/linux-user/m68k/target_resource.h b/linux-user/m68k/target_resource.h new file mode 100644 index 0000000000..227259594c --- /dev/null +++ b/linux-user/m68k/target_resource.h @@ -0,0 +1 @@ +#include "../generic/target_resource.h" diff --git a/linux-user/microblaze/target_resource.h b/linux-user/microblaze/target_resource.h new file mode 100644 index 0000000000..227259594c --- /dev/null +++ b/linux-user/microblaze/target_resource.h @@ -0,0 +1 @@ +#include "../generic/target_resource.h" diff --git a/linux-user/mips/target_resource.h b/linux-user/mips/target_resource.h new file mode 100644 index 0000000000..6d131b041d --- /dev/null +++ b/linux-user/mips/target_resource.h @@ -0,0 +1,24 @@ +#ifndef MIPS_TARGET_RESOURCE_H +#define MIPS_TARGET_RESOURCE_H + +#include "../generic/target_resource.h" + +#undef TARGET_RLIM_INFINITY +#define TARGET_RLIM_INFINITY 0x7fffffffUL + +#undef TARGET_RLIMIT_NOFILE +#define TARGET_RLIMIT_NOFILE 5 + +#undef TARGET_RLIMIT_AS +#define TARGET_RLIMIT_AS 6 + +#undef TARGET_RLIMIT_RSS +#define TARGET_RLIMIT_RSS 7 + +#undef TARGET_RLIMIT_NPROC +#define TARGET_RLIMIT_NPROC 8 + +#undef TARGET_RLIMIT_MEMLOCK +#define TARGET_RLIMIT_MEMLOCK 9 + +#endif diff --git a/linux-user/mips64/target_resource.h b/linux-user/mips64/target_resource.h new file mode 100644 index 0000000000..fe29002a12 --- /dev/null +++ b/linux-user/mips64/target_resource.h @@ -0,0 +1 @@ +#include "../mips/target_resource.h" diff --git a/linux-user/nios2/target_resource.h b/linux-user/nios2/target_resource.h new file mode 100644 index 0000000000..227259594c --- /dev/null +++ b/linux-user/nios2/target_resource.h @@ -0,0 +1 @@ +#include "../generic/target_resource.h" diff --git a/linux-user/openrisc/target_resource.h b/linux-user/openrisc/target_resource.h new file mode 100644 index 0000000000..227259594c --- /dev/null +++ b/linux-user/openrisc/target_resource.h @@ -0,0 +1 @@ +#include "../generic/target_resource.h" diff --git a/linux-user/ppc/target_resource.h b/linux-user/ppc/target_resource.h new file mode 100644 index 0000000000..227259594c --- /dev/null +++ b/linux-user/ppc/target_resource.h @@ -0,0 +1 @@ +#include "../generic/target_resource.h" diff --git a/linux-user/riscv/target_resource.h b/linux-user/riscv/target_resource.h new file mode 100644 index 0000000000..227259594c --- /dev/null +++ b/linux-user/riscv/target_resource.h @@ -0,0 +1 @@ +#include "../generic/target_resource.h" diff --git a/linux-user/s390x/target_resource.h b/linux-user/s390x/target_resource.h new file mode 100644 index 0000000000..227259594c --- /dev/null +++ b/linux-user/s390x/target_resource.h @@ -0,0 +1 @@ +#include "../generic/target_resource.h" diff --git a/linux-user/sh4/target_resource.h b/linux-user/sh4/target_resource.h new file mode 100644 index 0000000000..227259594c --- /dev/null +++ b/linux-user/sh4/target_resource.h @@ -0,0 +1 @@ +#include "../generic/target_resource.h" diff --git a/linux-user/sparc/target_resource.h b/linux-user/sparc/target_resource.h new file mode 100644 index 0000000000..d9a2fb814a --- /dev/null +++ b/linux-user/sparc/target_resource.h @@ -0,0 +1,17 @@ +#ifndef SPARC_TARGET_RESOURCE_H +#define SPARC_TARGET_RESOURCE_H + +#include "../generic/target_resource.h" + +#if TARGET_ABI_BITS == 32 +#undef TARGET_RLIM_INFINITY +#define TARGET_RLIM_INFINITY 0x7fffffffUL +#endif + +#undef TARGET_RLIMIT_NOFILE +#define TARGET_RLIMIT_NOFILE 6 + +#undef TARGET_RLIMIT_NPROC +#define TARGET_RLIMIT_NPROC 7 + +#endif diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h index c8690688b5..78607effe8 100644 --- a/linux-user/syscall_defs.h +++ b/linux-user/syscall_defs.h @@ -717,54 +717,7 @@ typedef struct target_siginfo { #define TARGET_TRAP_HWBKPT (4) /* hardware breakpoint/watchpoint */ #define TARGET_TRAP_UNK (5) /* undiagnosed trap */ -struct target_rlimit { - abi_ulong rlim_cur; - abi_ulong rlim_max; -}; - -#if defined(TARGET_ALPHA) -#define TARGET_RLIM_INFINITY 0x7fffffffffffffffull -#elif defined(TARGET_MIPS) || (defined(TARGET_SPARC) && TARGET_ABI_BITS == 32) -#define TARGET_RLIM_INFINITY 0x7fffffffUL -#else -#define TARGET_RLIM_INFINITY ((abi_ulong)-1) -#endif - -#define TARGET_RLIMIT_CPU 0 -#define TARGET_RLIMIT_FSIZE 1 -#define TARGET_RLIMIT_DATA 2 -#define TARGET_RLIMIT_STACK 3 -#define TARGET_RLIMIT_CORE 4 -#if defined(TARGET_MIPS) -#define TARGET_RLIMIT_NOFILE 5 -#define TARGET_RLIMIT_AS 6 -#define TARGET_RLIMIT_RSS 7 -#define TARGET_RLIMIT_NPROC 8 -#define TARGET_RLIMIT_MEMLOCK 9 -#elif defined(TARGET_ALPHA) -#define TARGET_RLIMIT_RSS 5 -#define TARGET_RLIMIT_NOFILE 6 -#define TARGET_RLIMIT_AS 7 -#define TARGET_RLIMIT_NPROC 8 -#define TARGET_RLIMIT_MEMLOCK 9 -#elif defined(TARGET_SPARC) -#define TARGET_RLIMIT_RSS 5 -#define TARGET_RLIMIT_NOFILE 6 -#define TARGET_RLIMIT_NPROC 7 -#define TARGET_RLIMIT_MEMLOCK 8 -#define TARGET_RLIMIT_AS 9 -#else -#define TARGET_RLIMIT_RSS 5 -#define TARGET_RLIMIT_NPROC 6 -#define TARGET_RLIMIT_NOFILE 7 -#define TARGET_RLIMIT_MEMLOCK 8 -#define TARGET_RLIMIT_AS 9 -#endif -#define TARGET_RLIMIT_LOCKS 10 -#define TARGET_RLIMIT_SIGPENDING 11 -#define TARGET_RLIMIT_MSGQUEUE 12 -#define TARGET_RLIMIT_NICE 13 -#define TARGET_RLIMIT_RTPRIO 14 +#include "target_resource.h" struct target_pollfd { int fd; /* file descriptor */ @@ -2769,10 +2722,6 @@ struct target_epoll_event { #define TARGET_EP_MAX_EVENTS (INT_MAX / sizeof(struct target_epoll_event)) #endif -struct target_rlimit64 { - uint64_t rlim_cur; - uint64_t rlim_max; -}; struct target_ucred { uint32_t pid; diff --git a/linux-user/x86_64/target_resource.h b/linux-user/x86_64/target_resource.h new file mode 100644 index 0000000000..227259594c --- /dev/null +++ b/linux-user/x86_64/target_resource.h @@ -0,0 +1 @@ +#include "../generic/target_resource.h" diff --git a/linux-user/xtensa/target_resource.h b/linux-user/xtensa/target_resource.h new file mode 100644 index 0000000000..227259594c --- /dev/null +++ b/linux-user/xtensa/target_resource.h @@ -0,0 +1 @@ +#include "../generic/target_resource.h" From 244fd08323088db73590ff2317dfe86f810b51d7 Mon Sep 17 00:00:00 2001 From: Serge Belyshev Date: Sat, 29 Jan 2022 22:48:23 +0300 Subject: [PATCH 186/460] linux-user/syscall: Translate TARGET_RLIMIT_RTTIME Signed-off-by: Serge Belyshev Reviewed-by: Laurent Vivier Reviewed-by: Laurent Vivier Message-Id: <87a6fel3w8.fsf_-_@depni.sinp.msu.ru> Signed-off-by: Laurent Vivier --- linux-user/generic/target_resource.h | 1 + linux-user/syscall.c | 2 ++ 2 files changed, 3 insertions(+) diff --git a/linux-user/generic/target_resource.h b/linux-user/generic/target_resource.h index f04c93b125..539d8c4677 100644 --- a/linux-user/generic/target_resource.h +++ b/linux-user/generic/target_resource.h @@ -33,5 +33,6 @@ struct target_rlimit64 { #define TARGET_RLIMIT_MSGQUEUE 12 #define TARGET_RLIMIT_NICE 13 #define TARGET_RLIMIT_RTPRIO 14 +#define TARGET_RLIMIT_RTTIME 15 #endif diff --git a/linux-user/syscall.c b/linux-user/syscall.c index b3948d13a9..b9b18a7eaf 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -1053,6 +1053,8 @@ static inline int target_to_host_resource(int code) return RLIMIT_RSS; case TARGET_RLIMIT_RTPRIO: return RLIMIT_RTPRIO; + case TARGET_RLIMIT_RTTIME: + return RLIMIT_RTTIME; case TARGET_RLIMIT_SIGPENDING: return RLIMIT_SIGPENDING; case TARGET_RLIMIT_STACK: From 0c83471bd75d329f4945e27dc1aa3a6cc2fda3bf Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 6 Dec 2021 15:34:04 +0100 Subject: [PATCH 187/460] tests/qemu-iotests: Fix 051 for binaries without 'lsi53c895a' The lsi53c895a SCSI adaptor might not be enabled in each and every x86 QEMU binary, e.g. it's disabled in the RHEL/CentOS build. Thus let's add a check to the 051 test so that it does not fail if this device is not available. Signed-off-by: Thomas Huth Message-Id: <20211206143404.247032-1-thuth@redhat.com> Signed-off-by: Hanna Reitz --- tests/qemu-iotests/051 | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/qemu-iotests/051 b/tests/qemu-iotests/051 index 1d2fa93a11..e9042a6214 100755 --- a/tests/qemu-iotests/051 +++ b/tests/qemu-iotests/051 @@ -45,6 +45,10 @@ _supported_proto file _unsupported_imgopts 'refcount_bits=\([^1]\|.\([^6]\|$\)\)' data_file _require_drivers nbd +if [ "$QEMU_DEFAULT_MACHINE" = "pc" ]; then + _require_devices lsi53c895a +fi + do_run_qemu() { echo Testing: "$@" From fc2c3996a59683685a663deb3af12183ad24e4a7 Mon Sep 17 00:00:00 2001 From: Hanna Reitz Date: Thu, 23 Dec 2021 17:53:08 +0100 Subject: [PATCH 188/460] iotests/MRCE: Write data to source This test assumes that mirror flushes the source when entering the READY state, and that the format level will pass that flush on to the protocol level (where we intercept it with blkdebug). However, apparently that does not happen when using a VMDK image with zeroed_grain=on, which actually is the default set by testenv.py. Right now, Python tests ignore IMGOPTS, though, so this has no effect; but Vladimir has a series that will change this, so we need to fix this test before that series lands. We can fix it by writing data to the source before we start the mirror job; apparently that makes the (VMDK) format layer change its mind and pass on the pre-READY flush to the protocol level, so the test passes again. (I presume, without any data written, mirror just does a 64M zero write on the target, which VMDK with zeroed_grain=on basically just ignores.) Without this, we do not get a flush, and so blkdebug only sees a single flush at the end of the job instead of two, and therefore does not inject an error, which makes the block job complete instead of raising an error. Signed-off-by: Hanna Reitz Message-Id: <20211223165308.103793-1-hreitz@redhat.com> Reviewed-by: Vladimir Sementsov-Ogievskiy --- tests/qemu-iotests/tests/mirror-ready-cancel-error | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/qemu-iotests/tests/mirror-ready-cancel-error b/tests/qemu-iotests/tests/mirror-ready-cancel-error index f2dc88881f..770ffca379 100755 --- a/tests/qemu-iotests/tests/mirror-ready-cancel-error +++ b/tests/qemu-iotests/tests/mirror-ready-cancel-error @@ -36,6 +36,11 @@ class TestMirrorReadyCancelError(iotests.QMPTestCase): assert iotests.qemu_img_create('-f', iotests.imgfmt, target, str(image_size)) == 0 + # Ensure that mirror will copy something before READY so the + # target format layer will forward the pre-READY flush to its + # file child + assert iotests.qemu_io_silent('-c', 'write -P 1 0 64k', source) == 0 + self.vm = iotests.VM() self.vm.launch() @@ -97,7 +102,7 @@ class TestMirrorReadyCancelError(iotests.QMPTestCase): # Write something so will not leave the job immediately, but # flush first (which will fail, thanks to blkdebug) res = self.vm.qmp('human-monitor-command', - command_line='qemu-io mirror-top "write 0 64k"') + command_line='qemu-io mirror-top "write -P 2 0 64k"') self.assert_qmp(res, 'return', '') # Drain status change events From 3bd2b942d9a8a10bb7a504a1ecf4a3e70803840e Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Thu, 23 Dec 2021 17:01:26 +0100 Subject: [PATCH 189/460] iotests.py: img_info_log(): rename imgopts argument We are going to support IMGOPTS environment variable like in bash tests. Corresponding global variable in iotests.py should be called imgopts. So to not interfere with function argument, rename it in advance. Signed-off-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Max Reitz Message-Id: <20211223160144.1097696-2-vsementsov@virtuozzo.com> Signed-off-by: Hanna Reitz --- tests/qemu-iotests/210 | 8 ++++---- tests/qemu-iotests/iotests.py | 5 +++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/tests/qemu-iotests/210 b/tests/qemu-iotests/210 index a4dcc5fe59..10b0a0b87c 100755 --- a/tests/qemu-iotests/210 +++ b/tests/qemu-iotests/210 @@ -62,7 +62,7 @@ with iotests.FilePath('t.luks') as disk_path, \ 'driver=luks,file.driver=file,file.filename=%s,key-secret=keysec0' % (disk_path), filter_path=disk_path, extra_args=['--object', 'secret,id=keysec0,data=foo'], - imgopts=True) + use_image_opts=True) # # Successful image creation (with non-default options) @@ -96,7 +96,7 @@ with iotests.FilePath('t.luks') as disk_path, \ 'driver=luks,file.driver=file,file.filename=%s,key-secret=keysec0' % (disk_path), filter_path=disk_path, extra_args=['--object', 'secret,id=keysec0,data=foo'], - imgopts=True) + use_image_opts=True) # # Invalid BlockdevRef @@ -132,7 +132,7 @@ with iotests.FilePath('t.luks') as disk_path, \ 'driver=luks,file.driver=file,file.filename=%s,key-secret=keysec0' % (disk_path), filter_path=disk_path, extra_args=['--object', 'secret,id=keysec0,data=foo'], - imgopts=True) + use_image_opts=True) # # Invalid sizes @@ -176,4 +176,4 @@ with iotests.FilePath('t.luks') as disk_path, \ 'driver=luks,file.driver=file,file.filename=%s,key-secret=keysec0' % (disk_path), filter_path=disk_path, extra_args=['--object', 'secret,id=keysec0,data=foo'], - imgopts=True) + use_image_opts=True) diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py index 1e2f2391d1..30a8837ea2 100644 --- a/tests/qemu-iotests/iotests.py +++ b/tests/qemu-iotests/iotests.py @@ -227,9 +227,10 @@ def qemu_img_log(*args): log(result, filters=[filter_testfiles]) return result -def img_info_log(filename, filter_path=None, imgopts=False, extra_args=()): +def img_info_log(filename, filter_path=None, use_image_opts=False, + extra_args=()): args = ['info'] - if imgopts: + if use_image_opts: args.append('--image-opts') else: args += ['-f', imgfmt] From 7c15400cdd06b7b9b26c86eac1858fb9c0d77c1c Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Thu, 23 Dec 2021 17:01:27 +0100 Subject: [PATCH 190/460] iotests.py: implement unsupported_imgopts We are going to support some addition IMGOPTS in python iotests like in bash iotests. Similarly to bash iotests, we want a way to skip some tests which can't work with specific IMGOPTS. Globally for python iotests we will not support things like 'data_file=$TEST_IMG.ext_data_file' in IMGOPTS, so, forbid this globally in iotests.py. Suggested-by: Hanna Reitz Signed-off-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Hanna Reitz Message-Id: <20211223160144.1097696-3-vsementsov@virtuozzo.com> Signed-off-by: Hanna Reitz --- tests/qemu-iotests/iotests.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py index 30a8837ea2..2fa5dcba76 100644 --- a/tests/qemu-iotests/iotests.py +++ b/tests/qemu-iotests/iotests.py @@ -1226,6 +1226,17 @@ def _verify_virtio_scsi_pci_or_ccw() -> None: notrun('Missing virtio-scsi-pci or virtio-scsi-ccw in QEMU binary') +def _verify_imgopts(unsupported: Sequence[str] = ()) -> None: + imgopts = os.environ.get('IMGOPTS') + # One of usage examples for IMGOPTS is "data_file=$TEST_IMG.ext_data_file" + # but it supported only for bash tests. We don't have a concept of global + # TEST_IMG in iotests.py, not saying about somehow parsing $variables. + # So, for simplicity let's just not support any IMGOPTS with '$' inside. + unsup = list(unsupported) + ['$'] + if imgopts and any(x in imgopts for x in unsup): + notrun(f'not suitable for this imgopts: {imgopts}') + + def supports_quorum(): return 'quorum' in qemu_img_pipe('--help') @@ -1402,7 +1413,8 @@ def execute_setup_common(supported_fmts: Sequence[str] = (), unsupported_fmts: Sequence[str] = (), supported_protocols: Sequence[str] = (), unsupported_protocols: Sequence[str] = (), - required_fmts: Sequence[str] = ()) -> bool: + required_fmts: Sequence[str] = (), + unsupported_imgopts: Sequence[str] = ()) -> bool: """ Perform necessary setup for either script-style or unittest-style tests. @@ -1422,6 +1434,7 @@ def execute_setup_common(supported_fmts: Sequence[str] = (), _verify_aio_mode(supported_aio_modes) _verify_formats(required_fmts) _verify_virtio_blk() + _verify_imgopts(unsupported_imgopts) return debug From b30b8077243ea5dc93a540eedfecee3c74b19fa2 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Thu, 23 Dec 2021 17:01:28 +0100 Subject: [PATCH 191/460] iotests: specify some unsupported_imgopts for python iotests We are going to support IMGOPTS for python iotests. Still some iotests will not work with common IMGOPTS used with bash iotests like specifying refcount_bits and compat qcow2 options. So we should define corresponding unsupported_imgopts for now. Signed-off-by: Vladimir Sementsov-Ogievskiy Message-Id: <20211223160144.1097696-4-vsementsov@virtuozzo.com> Signed-off-by: Hanna Reitz --- tests/qemu-iotests/044 | 3 ++- tests/qemu-iotests/065 | 3 ++- tests/qemu-iotests/163 | 3 ++- tests/qemu-iotests/165 | 3 ++- tests/qemu-iotests/196 | 3 ++- tests/qemu-iotests/242 | 3 ++- tests/qemu-iotests/246 | 3 ++- tests/qemu-iotests/254 | 3 ++- tests/qemu-iotests/260 | 3 ++- tests/qemu-iotests/274 | 3 ++- tests/qemu-iotests/281 | 3 ++- tests/qemu-iotests/303 | 3 ++- tests/qemu-iotests/tests/migrate-bitmaps-postcopy-test | 3 ++- tests/qemu-iotests/tests/migrate-bitmaps-test | 3 ++- tests/qemu-iotests/tests/remove-bitmap-from-backing | 3 ++- 15 files changed, 30 insertions(+), 15 deletions(-) diff --git a/tests/qemu-iotests/044 b/tests/qemu-iotests/044 index 64b18eb7c8..d696e6442a 100755 --- a/tests/qemu-iotests/044 +++ b/tests/qemu-iotests/044 @@ -117,4 +117,5 @@ class TestRefcountTableGrowth(iotests.QMPTestCase): if __name__ == '__main__': iotests.main(supported_fmts=['qcow2'], - supported_protocols=['file']) + supported_protocols=['file'], + unsupported_imgopts=['refcount_bits']) diff --git a/tests/qemu-iotests/065 b/tests/qemu-iotests/065 index 3c2ca27627..dc7716275f 100755 --- a/tests/qemu-iotests/065 +++ b/tests/qemu-iotests/065 @@ -139,4 +139,5 @@ TestQMP = None if __name__ == '__main__': iotests.main(supported_fmts=['qcow2'], - supported_protocols=['file']) + supported_protocols=['file'], + unsupported_imgopts=['refcount_bits']) diff --git a/tests/qemu-iotests/163 b/tests/qemu-iotests/163 index dedce8ef43..b8bfc95358 100755 --- a/tests/qemu-iotests/163 +++ b/tests/qemu-iotests/163 @@ -169,4 +169,5 @@ ShrinkBaseClass = None if __name__ == '__main__': iotests.main(supported_fmts=['raw', 'qcow2'], - supported_protocols=['file']) + supported_protocols=['file'], + unsupported_imgopts=['compat']) diff --git a/tests/qemu-iotests/165 b/tests/qemu-iotests/165 index ce499946b8..e3ef28e2ee 100755 --- a/tests/qemu-iotests/165 +++ b/tests/qemu-iotests/165 @@ -157,4 +157,5 @@ class TestPersistentDirtyBitmap(iotests.QMPTestCase): if __name__ == '__main__': iotests.main(supported_fmts=['qcow2'], - supported_protocols=['file']) + supported_protocols=['file'], + unsupported_imgopts=['compat']) diff --git a/tests/qemu-iotests/196 b/tests/qemu-iotests/196 index 2451515094..76509a5ad1 100755 --- a/tests/qemu-iotests/196 +++ b/tests/qemu-iotests/196 @@ -65,4 +65,5 @@ class TestInvalidateAutoclear(iotests.QMPTestCase): if __name__ == '__main__': iotests.main(supported_fmts=['qcow2'], - supported_protocols=['file']) + supported_protocols=['file'], + unsupported_imgopts=['compat']) diff --git a/tests/qemu-iotests/242 b/tests/qemu-iotests/242 index a9b27668c2..96a30152b0 100755 --- a/tests/qemu-iotests/242 +++ b/tests/qemu-iotests/242 @@ -26,7 +26,8 @@ from iotests import qemu_img_create, qemu_io, qemu_img_pipe, \ file_path, img_info_log, log, filter_qemu_io iotests.script_initialize(supported_fmts=['qcow2'], - supported_protocols=['file']) + supported_protocols=['file'], + unsupported_imgopts=['refcount_bits', 'compat']) disk = file_path('disk') chunk = 256 * 1024 diff --git a/tests/qemu-iotests/246 b/tests/qemu-iotests/246 index 5932a0e8a9..b009a78397 100755 --- a/tests/qemu-iotests/246 +++ b/tests/qemu-iotests/246 @@ -23,7 +23,8 @@ import iotests from iotests import log -iotests.script_initialize(supported_fmts=['qcow2']) +iotests.script_initialize(supported_fmts=['qcow2'], + unsupported_imgopts=['compat']) size = 64 * 1024 * 1024 * 1024 gran_small = 32 * 1024 gran_large = 128 * 1024 diff --git a/tests/qemu-iotests/254 b/tests/qemu-iotests/254 index 108bf5f894..7ea098818c 100755 --- a/tests/qemu-iotests/254 +++ b/tests/qemu-iotests/254 @@ -22,7 +22,8 @@ import iotests from iotests import qemu_img_create, file_path, log -iotests.script_initialize(supported_fmts=['qcow2']) +iotests.script_initialize(supported_fmts=['qcow2'], + unsupported_imgopts=['compat']) disk, top = file_path('disk', 'top') size = 1024 * 1024 diff --git a/tests/qemu-iotests/260 b/tests/qemu-iotests/260 index 2ec64a9b99..c2133f9980 100755 --- a/tests/qemu-iotests/260 +++ b/tests/qemu-iotests/260 @@ -23,7 +23,8 @@ import iotests from iotests import qemu_img_create, file_path, log, filter_qmp_event iotests.script_initialize( - supported_fmts=['qcow2'] + supported_fmts=['qcow2'], + unsupported_imgopts=['compat'] ) base, top = file_path('base', 'top') diff --git a/tests/qemu-iotests/274 b/tests/qemu-iotests/274 index caab008e07..080a90f10f 100755 --- a/tests/qemu-iotests/274 +++ b/tests/qemu-iotests/274 @@ -23,7 +23,8 @@ import iotests iotests.script_initialize(supported_fmts=['qcow2'], - supported_platforms=['linux']) + supported_platforms=['linux'], + unsupported_imgopts=['refcount_bits', 'compat']) size_short = 1 * 1024 * 1024 size_long = 2 * 1024 * 1024 diff --git a/tests/qemu-iotests/281 b/tests/qemu-iotests/281 index 956698083f..318e333939 100755 --- a/tests/qemu-iotests/281 +++ b/tests/qemu-iotests/281 @@ -245,4 +245,5 @@ class TestBlockdevBackupAbort(iotests.QMPTestCase): if __name__ == '__main__': iotests.main(supported_fmts=['qcow2'], - supported_protocols=['file']) + supported_protocols=['file'], + unsupported_imgopts=['compat']) diff --git a/tests/qemu-iotests/303 b/tests/qemu-iotests/303 index 425544c064..475cb5428d 100755 --- a/tests/qemu-iotests/303 +++ b/tests/qemu-iotests/303 @@ -23,7 +23,8 @@ import iotests import subprocess from iotests import qemu_img_create, qemu_io, file_path, log, filter_qemu_io -iotests.script_initialize(supported_fmts=['qcow2']) +iotests.script_initialize(supported_fmts=['qcow2'], + unsupported_imgopts=['refcount_bits', 'compat']) disk = file_path('disk') chunk = 1024 * 1024 diff --git a/tests/qemu-iotests/tests/migrate-bitmaps-postcopy-test b/tests/qemu-iotests/tests/migrate-bitmaps-postcopy-test index 00ebb5c251..fc9c4b4ef4 100755 --- a/tests/qemu-iotests/tests/migrate-bitmaps-postcopy-test +++ b/tests/qemu-iotests/tests/migrate-bitmaps-postcopy-test @@ -272,4 +272,5 @@ class TestDirtyBitmapPostcopyMigration(iotests.QMPTestCase): if __name__ == '__main__': - iotests.main(supported_fmts=['qcow2']) + iotests.main(supported_fmts=['qcow2'], + unsupported_imgopts=['compat']) diff --git a/tests/qemu-iotests/tests/migrate-bitmaps-test b/tests/qemu-iotests/tests/migrate-bitmaps-test index c23df3d75c..59f3357580 100755 --- a/tests/qemu-iotests/tests/migrate-bitmaps-test +++ b/tests/qemu-iotests/tests/migrate-bitmaps-test @@ -307,7 +307,8 @@ def main() -> None: iotests.main( supported_fmts=['qcow2'], - supported_protocols=['file'] + supported_protocols=['file'], + unsupported_imgopts=['compat'] ) diff --git a/tests/qemu-iotests/tests/remove-bitmap-from-backing b/tests/qemu-iotests/tests/remove-bitmap-from-backing index 8d48fc0f3c..3c397b08ea 100755 --- a/tests/qemu-iotests/tests/remove-bitmap-from-backing +++ b/tests/qemu-iotests/tests/remove-bitmap-from-backing @@ -21,7 +21,8 @@ import iotests from iotests import log, qemu_img_create, qemu_img, qemu_img_pipe -iotests.script_initialize(supported_fmts=['qcow2']) +iotests.script_initialize(supported_fmts=['qcow2'], + unsupported_imgopts=['compat']) top, base = iotests.file_path('top', 'base') size = '1M' From 22e29bcea12ccf0e127b91917d959c69bebbd952 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Thu, 23 Dec 2021 17:01:29 +0100 Subject: [PATCH 192/460] iotests.py: qemu_img*("create"): support IMGOPTS='compression_type=zstd' Adding support of IMGOPTS (like in bash tests) allows user to pass a lot of different options. Still, some may require additional logic. Now we want compression_type option, so add some smart logic around it: ignore compression_type=zstd in IMGOPTS, if test want qcow2 in compatibility mode. As well, ignore compression_type for non-qcow2 formats. Note that we may instead add support only to qemu_img_create(), but that works bad: 1. We'll have to update a lot of tests to use qemu_img_create instead of qemu_img('create'). (still, we may want do it anyway, but no reason to create a dependancy between task of supporting IMGOPTS and updating a lot of tests) 2. Some tests use qemu_img_pipe('create', ..) - even more work on updating 3. Even if we update all tests to go through qemu_img_create, we'll need a way to avoid creating new tests using qemu_img*('create') - add assertions.. That doesn't seem good. So, let's add support of IMGOPTS to most generic qemu_img_pipe_and_status(). Signed-off-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Hanna Reitz Message-Id: <20211223160144.1097696-5-vsementsov@virtuozzo.com> Signed-off-by: Hanna Reitz --- tests/qemu-iotests/iotests.py | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py index 2fa5dcba76..740f8be36b 100644 --- a/tests/qemu-iotests/iotests.py +++ b/tests/qemu-iotests/iotests.py @@ -16,6 +16,7 @@ # along with this program. If not, see . # +import argparse import atexit import bz2 from collections import OrderedDict @@ -161,11 +162,35 @@ def qemu_tool_pipe_and_status(tool: str, args: Sequence[str], {-subp.returncode}: {cmd}\n') return (output, subp.returncode) +def qemu_img_create_prepare_args(args: List[str]) -> List[str]: + if not args or args[0] != 'create': + return list(args) + args = args[1:] + + p = argparse.ArgumentParser(allow_abbrev=False) + p.add_argument('-f') + parsed, remaining = p.parse_known_args(args) + + result = ['create'] + if parsed.f is not None: + result += ['-f', parsed.f] + + # IMGOPTS most probably contain options specific for the selected format, + # like extended_l2 or compression_type for qcow2. Test may want to create + # additional images in other formats that doesn't support these options. + # So, use IMGOPTS only for images created in imgfmt format. + if parsed.f == imgfmt and 'IMGOPTS' in os.environ: + result += ['-o', os.environ['IMGOPTS']] + + result += remaining + + return result + def qemu_img_pipe_and_status(*args: str) -> Tuple[str, int]: """ Run qemu-img and return both its output and its exit code """ - full_args = qemu_img_args + list(args) + full_args = qemu_img_args + qemu_img_create_prepare_args(list(args)) return qemu_tool_pipe_and_status('qemu-img', full_args) def qemu_img(*args: str) -> int: From 8f9e54ccfd047cbef09fe10fa75d59333052fb78 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Thu, 23 Dec 2021 17:01:30 +0100 Subject: [PATCH 193/460] iotests: drop qemu_img_verbose() helper qemu_img_verbose() has a drawback of not going through generic qemu_img_pipe_and_status(). qemu_img_verbose() is not very popular, so update the only two users to qemu_img_log() and drop qemu_img_verbose() at all. Signed-off-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Hanna Reitz Message-Id: <20211223160144.1097696-6-vsementsov@virtuozzo.com> Signed-off-by: Hanna Reitz --- tests/qemu-iotests/044 | 5 +++-- tests/qemu-iotests/044.out | 1 + tests/qemu-iotests/209 | 7 ++++--- tests/qemu-iotests/209.out | 2 ++ tests/qemu-iotests/iotests.py | 8 -------- 5 files changed, 10 insertions(+), 13 deletions(-) diff --git a/tests/qemu-iotests/044 b/tests/qemu-iotests/044 index d696e6442a..a5ee9a7ded 100755 --- a/tests/qemu-iotests/044 +++ b/tests/qemu-iotests/044 @@ -24,7 +24,7 @@ import os import qcow2 from qcow2 import QcowHeader import iotests -from iotests import qemu_img, qemu_img_verbose, qemu_io +from iotests import qemu_img, qemu_img_log, qemu_io import struct import subprocess import sys @@ -112,10 +112,11 @@ class TestRefcountTableGrowth(iotests.QMPTestCase): def test_grow_refcount_table(self): qemu_io('-c', 'write 3800M 1M', test_img) - qemu_img_verbose('check' , test_img) + qemu_img_log('check' , test_img) pass if __name__ == '__main__': + iotests.activate_logging() iotests.main(supported_fmts=['qcow2'], supported_protocols=['file'], unsupported_imgopts=['refcount_bits']) diff --git a/tests/qemu-iotests/044.out b/tests/qemu-iotests/044.out index 703cf3dee1..ff663b17d7 100644 --- a/tests/qemu-iotests/044.out +++ b/tests/qemu-iotests/044.out @@ -1,6 +1,7 @@ No errors were found on the image. 7292415/33554432 = 21.73% allocated, 0.00% fragmented, 0.00% compressed clusters Image end offset: 4296217088 + . ---------------------------------------------------------------------- Ran 1 tests diff --git a/tests/qemu-iotests/209 b/tests/qemu-iotests/209 index ff7efea11b..f6ad08ec42 100755 --- a/tests/qemu-iotests/209 +++ b/tests/qemu-iotests/209 @@ -20,8 +20,8 @@ # import iotests -from iotests import qemu_img_create, qemu_io, qemu_img_verbose, qemu_nbd, \ - file_path +from iotests import qemu_img_create, qemu_io, qemu_img_log, qemu_nbd, \ + file_path, log iotests.script_initialize(supported_fmts=['qcow2']) @@ -33,4 +33,5 @@ qemu_img_create('-f', iotests.imgfmt, disk, '1M') qemu_io('-f', iotests.imgfmt, '-c', 'write 0 512K', disk) qemu_nbd('-k', nbd_sock, '-x', 'exp', '-f', iotests.imgfmt, disk) -qemu_img_verbose('map', '-f', 'raw', '--output=json', nbd_uri) +qemu_img_log('map', '-f', 'raw', '--output=json', nbd_uri) +log('done.') # avoid new line at the end of output file diff --git a/tests/qemu-iotests/209.out b/tests/qemu-iotests/209.out index f27be3fa7b..515906ac7a 100644 --- a/tests/qemu-iotests/209.out +++ b/tests/qemu-iotests/209.out @@ -1,2 +1,4 @@ [{ "start": 0, "length": 524288, "depth": 0, "present": true, "zero": false, "data": true, "offset": 0}, { "start": 524288, "length": 524288, "depth": 0, "present": true, "zero": true, "data": false, "offset": 524288}] + +done. diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py index 740f8be36b..cc4bbbcf7b 100644 --- a/tests/qemu-iotests/iotests.py +++ b/tests/qemu-iotests/iotests.py @@ -235,14 +235,6 @@ def qemu_img_measure(*args): def qemu_img_check(*args): return json.loads(qemu_img_pipe("check", "--output", "json", *args)) -def qemu_img_verbose(*args): - '''Run qemu-img without suppressing its output and return the exit code''' - exitcode = subprocess.call(qemu_img_args + list(args)) - if exitcode < 0: - sys.stderr.write('qemu-img received signal %i: %s\n' - % (-exitcode, ' '.join(qemu_img_args + list(args)))) - return exitcode - def qemu_img_pipe(*args: str) -> str: '''Run qemu-img and return its output''' return qemu_img_pipe_and_status(*args)[0] From 28a5ad93da08ae55c8dfac0db8615936ca14b822 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Thu, 23 Dec 2021 17:01:31 +0100 Subject: [PATCH 194/460] iotests.py: rewrite default luks support in qemu_img Move the logic to more generic qemu_img_pipe_and_status(). Also behave better when we have several -o options. And reuse argument parser of course. Signed-off-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Hanna Reitz Message-Id: <20211223160144.1097696-7-vsementsov@virtuozzo.com> Signed-off-by: Hanna Reitz --- tests/qemu-iotests/iotests.py | 36 +++++++++++++++++------------------ 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py index cc4bbbcf7b..c382c527c8 100644 --- a/tests/qemu-iotests/iotests.py +++ b/tests/qemu-iotests/iotests.py @@ -168,9 +168,13 @@ def qemu_img_create_prepare_args(args: List[str]) -> List[str]: args = args[1:] p = argparse.ArgumentParser(allow_abbrev=False) + # -o option may be specified several times + p.add_argument('-o', action='append', default=[]) p.add_argument('-f') parsed, remaining = p.parse_known_args(args) + opts_list = parsed.o + result = ['create'] if parsed.f is not None: result += ['-f', parsed.f] @@ -179,8 +183,18 @@ def qemu_img_create_prepare_args(args: List[str]) -> List[str]: # like extended_l2 or compression_type for qcow2. Test may want to create # additional images in other formats that doesn't support these options. # So, use IMGOPTS only for images created in imgfmt format. - if parsed.f == imgfmt and 'IMGOPTS' in os.environ: - result += ['-o', os.environ['IMGOPTS']] + imgopts = os.environ.get('IMGOPTS') + if imgopts and parsed.f == imgfmt: + opts_list.insert(0, imgopts) + + # default luks support + if parsed.f == 'luks' and \ + all('key-secret' not in opts for opts in opts_list): + result += ['--object', luks_default_secret_object] + opts_list.append(luks_default_key_secret_opt) + + for opts in opts_list: + result += ['-o', opts] result += remaining @@ -211,23 +225,7 @@ def ordered_qmp(qmsg, conv_keys=True): return qmsg def qemu_img_create(*args): - args = list(args) - - # default luks support - if '-f' in args and args[args.index('-f') + 1] == 'luks': - if '-o' in args: - i = args.index('-o') - if 'key-secret' not in args[i + 1]: - args[i + 1].append(luks_default_key_secret_opt) - args.insert(i + 2, '--object') - args.insert(i + 3, luks_default_secret_object) - else: - args = ['-o', luks_default_key_secret_opt, - '--object', luks_default_secret_object] + args - - args.insert(0, 'create') - - return qemu_img(*args) + return qemu_img('create', *args) def qemu_img_measure(*args): return json.loads(qemu_img_pipe("measure", "--output", "json", *args)) From 677e0bae686e7c670a71d1f6a491a7f06f77de73 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Thu, 23 Dec 2021 17:01:32 +0100 Subject: [PATCH 195/460] iotest 303: explicit compression type The test prints qcow2 header fields which depends on chosen compression type. So, let's be explicit in what compression type we want and independent of IMGOPTS. Test both existing compression types. Signed-off-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Max Reitz Message-Id: <20211223160144.1097696-8-vsementsov@virtuozzo.com> Signed-off-by: Hanna Reitz --- tests/qemu-iotests/303 | 23 +++++++++++++++-------- tests/qemu-iotests/303.out | 30 +++++++++++++++++++++++++++++- 2 files changed, 44 insertions(+), 9 deletions(-) diff --git a/tests/qemu-iotests/303 b/tests/qemu-iotests/303 index 475cb5428d..16c2e10827 100755 --- a/tests/qemu-iotests/303 +++ b/tests/qemu-iotests/303 @@ -54,12 +54,19 @@ def add_bitmap(num, begin, end, disabled): log('') -qemu_img_create('-f', iotests.imgfmt, disk, '10M') +def test(compression_type: str, json_output: bool) -> None: + qemu_img_create('-f', iotests.imgfmt, + '-o', f'compression_type={compression_type}', + disk, '10M') + add_bitmap(1, 0, 6, False) + add_bitmap(2, 6, 8, True) -add_bitmap(1, 0, 6, False) -add_bitmap(2, 6, 8, True) -dump = ['./qcow2.py', disk, 'dump-header'] -subprocess.run(dump) -# Dump the metadata in JSON format -dump.append('-j') -subprocess.run(dump) + cmd = ['./qcow2.py', disk, 'dump-header'] + if json_output: + cmd.append('-j') + + subprocess.run(cmd) + + +test('zlib', False) +test('zstd', True) diff --git a/tests/qemu-iotests/303.out b/tests/qemu-iotests/303.out index 7c16998587..b3c70827b7 100644 --- a/tests/qemu-iotests/303.out +++ b/tests/qemu-iotests/303.out @@ -80,6 +80,34 @@ extra_data_size 0 Bitmap table type size offset 0 all-zeroes 0 0 +Add bitmap 1 +wrote 1048576/1048576 bytes at offset 0 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +wrote 1048576/1048576 bytes at offset 1048576 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +wrote 1048576/1048576 bytes at offset 2097152 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +wrote 1048576/1048576 bytes at offset 3145728 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +wrote 1048576/1048576 bytes at offset 4194304 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +wrote 1048576/1048576 bytes at offset 5242880 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + + +Add bitmap 2 +wrote 1048576/1048576 bytes at offset 6291456 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +wrote 1048576/1048576 bytes at offset 7340032 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + + { "magic": 1363560955, "version": 3, @@ -94,7 +122,7 @@ Bitmap table type size offset "refcount_table_clusters": 1, "nb_snapshots": 0, "snapshot_offset": 0, - "incompatible_features": 0, + "incompatible_features": 8, "compatible_features": 0, "autoclear_features": 1, "refcount_order": 4, From 12a936171d71f839dc907fff7887358a05ac20f8 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Thu, 23 Dec 2021 17:01:33 +0100 Subject: [PATCH 196/460] iotest 065: explicit compression type The test checks different options. It of course fails if set IMGOPTS='compression_type=zstd'. So, let's be explicit in what compression type we want and independent of IMGOPTS. Test both existing compression types. Signed-off-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Hanna Reitz Message-Id: <20211223160144.1097696-9-vsementsov@virtuozzo.com> Signed-off-by: Hanna Reitz --- tests/qemu-iotests/065 | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/qemu-iotests/065 b/tests/qemu-iotests/065 index dc7716275f..f7c1b68dad 100755 --- a/tests/qemu-iotests/065 +++ b/tests/qemu-iotests/065 @@ -88,7 +88,7 @@ class TestQMP(TestImageInfoSpecific): class TestQCow2(TestQemuImgInfo): '''Testing a qcow2 version 2 image''' - img_options = 'compat=0.10' + img_options = 'compat=0.10,compression_type=zlib' json_compare = { 'compat': '0.10', 'refcount-bits': 16, 'compression-type': 'zlib' } human_compare = [ 'compat: 0.10', 'compression type: zlib', @@ -96,17 +96,17 @@ class TestQCow2(TestQemuImgInfo): class TestQCow3NotLazy(TestQemuImgInfo): '''Testing a qcow2 version 3 image with lazy refcounts disabled''' - img_options = 'compat=1.1,lazy_refcounts=off' + img_options = 'compat=1.1,lazy_refcounts=off,compression_type=zstd' json_compare = { 'compat': '1.1', 'lazy-refcounts': False, 'refcount-bits': 16, 'corrupt': False, - 'compression-type': 'zlib', 'extended-l2': False } - human_compare = [ 'compat: 1.1', 'compression type: zlib', + 'compression-type': 'zstd', 'extended-l2': False } + human_compare = [ 'compat: 1.1', 'compression type: zstd', 'lazy refcounts: false', 'refcount bits: 16', 'corrupt: false', 'extended l2: false' ] class TestQCow3Lazy(TestQemuImgInfo): '''Testing a qcow2 version 3 image with lazy refcounts enabled''' - img_options = 'compat=1.1,lazy_refcounts=on' + img_options = 'compat=1.1,lazy_refcounts=on,compression_type=zlib' json_compare = { 'compat': '1.1', 'lazy-refcounts': True, 'refcount-bits': 16, 'corrupt': False, 'compression-type': 'zlib', 'extended-l2': False } @@ -117,7 +117,7 @@ class TestQCow3Lazy(TestQemuImgInfo): class TestQCow3NotLazyQMP(TestQMP): '''Testing a qcow2 version 3 image with lazy refcounts disabled, opening with lazy refcounts enabled''' - img_options = 'compat=1.1,lazy_refcounts=off' + img_options = 'compat=1.1,lazy_refcounts=off,compression_type=zlib' qemu_options = 'lazy-refcounts=on' compare = { 'compat': '1.1', 'lazy-refcounts': False, 'refcount-bits': 16, 'corrupt': False, @@ -127,11 +127,11 @@ class TestQCow3NotLazyQMP(TestQMP): class TestQCow3LazyQMP(TestQMP): '''Testing a qcow2 version 3 image with lazy refcounts enabled, opening with lazy refcounts disabled''' - img_options = 'compat=1.1,lazy_refcounts=on' + img_options = 'compat=1.1,lazy_refcounts=on,compression_type=zstd' qemu_options = 'lazy-refcounts=off' compare = { 'compat': '1.1', 'lazy-refcounts': True, 'refcount-bits': 16, 'corrupt': False, - 'compression-type': 'zlib', 'extended-l2': False } + 'compression-type': 'zstd', 'extended-l2': False } TestImageInfoSpecific = None TestQemuImgInfo = None From a70eeb3d4725ce6c0d18e57ea952ec9e2b09e69c Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Thu, 23 Dec 2021 17:01:34 +0100 Subject: [PATCH 197/460] iotests.py: filter out successful output of qemu-img create The only "feature" of this "Formatting ..." line is that we have to update it every time we add new option. Let's drop it. Signed-off-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Hanna Reitz Message-Id: <20211223160144.1097696-10-vsementsov@virtuozzo.com> Signed-off-by: Hanna Reitz --- tests/qemu-iotests/149.out | 21 --------------------- tests/qemu-iotests/237.out | 3 --- tests/qemu-iotests/255.out | 4 ---- tests/qemu-iotests/274.out | 29 ----------------------------- tests/qemu-iotests/280.out | 1 - tests/qemu-iotests/296.out | 10 +++------- tests/qemu-iotests/iotests.py | 10 ++++++++-- 7 files changed, 11 insertions(+), 67 deletions(-) diff --git a/tests/qemu-iotests/149.out b/tests/qemu-iotests/149.out index 6877ab6c4a..ab879596ce 100644 --- a/tests/qemu-iotests/149.out +++ b/tests/qemu-iotests/149.out @@ -61,7 +61,6 @@ unlink TEST_DIR/luks-aes-256-xts-plain64-sha1.img # ================= qemu-img aes-256-xts-plain64-sha1 ================= # Create image qemu-img create -f luks --object secret,id=sec0,data=MTIzNDU2,format=base64 -o key-secret=sec0,iter-time=10,cipher-alg=aes-256,cipher-mode=xts,ivgen-alg=plain64,hash-alg=sha1 TEST_DIR/luks-aes-256-xts-plain64-sha1.img 4194304M -Formatting 'TEST_DIR/luks-aes-256-xts-plain64-sha1.img', fmt=luks size=4398046511104 key-secret=sec0 cipher-alg=aes-256 cipher-mode=xts ivgen-alg=plain64 hash-alg=sha1 iter-time=10 # Open dev sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-sha1.img qiotest-145-aes-256-xts-plain64-sha1 @@ -181,7 +180,6 @@ unlink TEST_DIR/luks-twofish-256-xts-plain64-sha1.img # ================= qemu-img twofish-256-xts-plain64-sha1 ================= # Create image qemu-img create -f luks --object secret,id=sec0,data=MTIzNDU2,format=base64 -o key-secret=sec0,iter-time=10,cipher-alg=twofish-256,cipher-mode=xts,ivgen-alg=plain64,hash-alg=sha1 TEST_DIR/luks-twofish-256-xts-plain64-sha1.img 4194304M -Formatting 'TEST_DIR/luks-twofish-256-xts-plain64-sha1.img', fmt=luks size=4398046511104 key-secret=sec0 cipher-alg=twofish-256 cipher-mode=xts ivgen-alg=plain64 hash-alg=sha1 iter-time=10 # Open dev sudo cryptsetup -q -v luksOpen TEST_DIR/luks-twofish-256-xts-plain64-sha1.img qiotest-145-twofish-256-xts-plain64-sha1 @@ -301,7 +299,6 @@ unlink TEST_DIR/luks-serpent-256-xts-plain64-sha1.img # ================= qemu-img serpent-256-xts-plain64-sha1 ================= # Create image qemu-img create -f luks --object secret,id=sec0,data=MTIzNDU2,format=base64 -o key-secret=sec0,iter-time=10,cipher-alg=serpent-256,cipher-mode=xts,ivgen-alg=plain64,hash-alg=sha1 TEST_DIR/luks-serpent-256-xts-plain64-sha1.img 4194304M -Formatting 'TEST_DIR/luks-serpent-256-xts-plain64-sha1.img', fmt=luks size=4398046511104 key-secret=sec0 cipher-alg=serpent-256 cipher-mode=xts ivgen-alg=plain64 hash-alg=sha1 iter-time=10 # Open dev sudo cryptsetup -q -v luksOpen TEST_DIR/luks-serpent-256-xts-plain64-sha1.img qiotest-145-serpent-256-xts-plain64-sha1 @@ -421,7 +418,6 @@ unlink TEST_DIR/luks-cast5-128-cbc-plain64-sha1.img # ================= qemu-img cast5-128-cbc-plain64-sha1 ================= # Create image qemu-img create -f luks --object secret,id=sec0,data=MTIzNDU2,format=base64 -o key-secret=sec0,iter-time=10,cipher-alg=cast5-128,cipher-mode=cbc,ivgen-alg=plain64,hash-alg=sha1 TEST_DIR/luks-cast5-128-cbc-plain64-sha1.img 4194304M -Formatting 'TEST_DIR/luks-cast5-128-cbc-plain64-sha1.img', fmt=luks size=4398046511104 key-secret=sec0 cipher-alg=cast5-128 cipher-mode=cbc ivgen-alg=plain64 hash-alg=sha1 iter-time=10 # Open dev sudo cryptsetup -q -v luksOpen TEST_DIR/luks-cast5-128-cbc-plain64-sha1.img qiotest-145-cast5-128-cbc-plain64-sha1 @@ -542,7 +538,6 @@ unlink TEST_DIR/luks-aes-256-cbc-plain-sha1.img # ================= qemu-img aes-256-cbc-plain-sha1 ================= # Create image qemu-img create -f luks --object secret,id=sec0,data=MTIzNDU2,format=base64 -o key-secret=sec0,iter-time=10,cipher-alg=aes-256,cipher-mode=cbc,ivgen-alg=plain,hash-alg=sha1 TEST_DIR/luks-aes-256-cbc-plain-sha1.img 4194304M -Formatting 'TEST_DIR/luks-aes-256-cbc-plain-sha1.img', fmt=luks size=4398046511104 key-secret=sec0 cipher-alg=aes-256 cipher-mode=cbc ivgen-alg=plain hash-alg=sha1 iter-time=10 # Open dev sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-plain-sha1.img qiotest-145-aes-256-cbc-plain-sha1 @@ -662,7 +657,6 @@ unlink TEST_DIR/luks-aes-256-cbc-plain64-sha1.img # ================= qemu-img aes-256-cbc-plain64-sha1 ================= # Create image qemu-img create -f luks --object secret,id=sec0,data=MTIzNDU2,format=base64 -o key-secret=sec0,iter-time=10,cipher-alg=aes-256,cipher-mode=cbc,ivgen-alg=plain64,hash-alg=sha1 TEST_DIR/luks-aes-256-cbc-plain64-sha1.img 4194304M -Formatting 'TEST_DIR/luks-aes-256-cbc-plain64-sha1.img', fmt=luks size=4398046511104 key-secret=sec0 cipher-alg=aes-256 cipher-mode=cbc ivgen-alg=plain64 hash-alg=sha1 iter-time=10 # Open dev sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-plain64-sha1.img qiotest-145-aes-256-cbc-plain64-sha1 @@ -782,7 +776,6 @@ unlink TEST_DIR/luks-aes-256-cbc-essiv-sha256-sha1.img # ================= qemu-img aes-256-cbc-essiv-sha256-sha1 ================= # Create image qemu-img create -f luks --object secret,id=sec0,data=MTIzNDU2,format=base64 -o key-secret=sec0,iter-time=10,cipher-alg=aes-256,cipher-mode=cbc,ivgen-alg=essiv,hash-alg=sha1,ivgen-hash-alg=sha256 TEST_DIR/luks-aes-256-cbc-essiv-sha256-sha1.img 4194304M -Formatting 'TEST_DIR/luks-aes-256-cbc-essiv-sha256-sha1.img', fmt=luks size=4398046511104 key-secret=sec0 cipher-alg=aes-256 cipher-mode=cbc ivgen-alg=essiv ivgen-hash-alg=sha256 hash-alg=sha1 iter-time=10 # Open dev sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-essiv-sha256-sha1.img qiotest-145-aes-256-cbc-essiv-sha256-sha1 @@ -902,7 +895,6 @@ unlink TEST_DIR/luks-aes-256-xts-essiv-sha256-sha1.img # ================= qemu-img aes-256-xts-essiv-sha256-sha1 ================= # Create image qemu-img create -f luks --object secret,id=sec0,data=MTIzNDU2,format=base64 -o key-secret=sec0,iter-time=10,cipher-alg=aes-256,cipher-mode=xts,ivgen-alg=essiv,hash-alg=sha1,ivgen-hash-alg=sha256 TEST_DIR/luks-aes-256-xts-essiv-sha256-sha1.img 4194304M -Formatting 'TEST_DIR/luks-aes-256-xts-essiv-sha256-sha1.img', fmt=luks size=4398046511104 key-secret=sec0 cipher-alg=aes-256 cipher-mode=xts ivgen-alg=essiv ivgen-hash-alg=sha256 hash-alg=sha1 iter-time=10 # Open dev sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-essiv-sha256-sha1.img qiotest-145-aes-256-xts-essiv-sha256-sha1 @@ -1022,7 +1014,6 @@ unlink TEST_DIR/luks-aes-128-xts-plain64-sha256-sha1.img # ================= qemu-img aes-128-xts-plain64-sha256-sha1 ================= # Create image qemu-img create -f luks --object secret,id=sec0,data=MTIzNDU2,format=base64 -o key-secret=sec0,iter-time=10,cipher-alg=aes-128,cipher-mode=xts,ivgen-alg=plain64,hash-alg=sha1 TEST_DIR/luks-aes-128-xts-plain64-sha256-sha1.img 4194304M -Formatting 'TEST_DIR/luks-aes-128-xts-plain64-sha256-sha1.img', fmt=luks size=4398046511104 key-secret=sec0 cipher-alg=aes-128 cipher-mode=xts ivgen-alg=plain64 hash-alg=sha1 iter-time=10 # Open dev sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-128-xts-plain64-sha256-sha1.img qiotest-145-aes-128-xts-plain64-sha256-sha1 @@ -1142,7 +1133,6 @@ unlink TEST_DIR/luks-aes-192-xts-plain64-sha256-sha1.img # ================= qemu-img aes-192-xts-plain64-sha256-sha1 ================= # Create image qemu-img create -f luks --object secret,id=sec0,data=MTIzNDU2,format=base64 -o key-secret=sec0,iter-time=10,cipher-alg=aes-192,cipher-mode=xts,ivgen-alg=plain64,hash-alg=sha1 TEST_DIR/luks-aes-192-xts-plain64-sha256-sha1.img 4194304M -Formatting 'TEST_DIR/luks-aes-192-xts-plain64-sha256-sha1.img', fmt=luks size=4398046511104 key-secret=sec0 cipher-alg=aes-192 cipher-mode=xts ivgen-alg=plain64 hash-alg=sha1 iter-time=10 # Open dev sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-192-xts-plain64-sha256-sha1.img qiotest-145-aes-192-xts-plain64-sha256-sha1 @@ -1262,7 +1252,6 @@ unlink TEST_DIR/luks-twofish-128-xts-plain64-sha1.img # ================= qemu-img twofish-128-xts-plain64-sha1 ================= # Create image qemu-img create -f luks --object secret,id=sec0,data=MTIzNDU2,format=base64 -o key-secret=sec0,iter-time=10,cipher-alg=twofish-128,cipher-mode=xts,ivgen-alg=plain64,hash-alg=sha1 TEST_DIR/luks-twofish-128-xts-plain64-sha1.img 4194304M -Formatting 'TEST_DIR/luks-twofish-128-xts-plain64-sha1.img', fmt=luks size=4398046511104 key-secret=sec0 cipher-alg=twofish-128 cipher-mode=xts ivgen-alg=plain64 hash-alg=sha1 iter-time=10 # Open dev sudo cryptsetup -q -v luksOpen TEST_DIR/luks-twofish-128-xts-plain64-sha1.img qiotest-145-twofish-128-xts-plain64-sha1 @@ -1383,7 +1372,6 @@ unlink TEST_DIR/luks-serpent-128-xts-plain64-sha1.img # ================= qemu-img serpent-128-xts-plain64-sha1 ================= # Create image qemu-img create -f luks --object secret,id=sec0,data=MTIzNDU2,format=base64 -o key-secret=sec0,iter-time=10,cipher-alg=serpent-128,cipher-mode=xts,ivgen-alg=plain64,hash-alg=sha1 TEST_DIR/luks-serpent-128-xts-plain64-sha1.img 4194304M -Formatting 'TEST_DIR/luks-serpent-128-xts-plain64-sha1.img', fmt=luks size=4398046511104 key-secret=sec0 cipher-alg=serpent-128 cipher-mode=xts ivgen-alg=plain64 hash-alg=sha1 iter-time=10 # Open dev sudo cryptsetup -q -v luksOpen TEST_DIR/luks-serpent-128-xts-plain64-sha1.img qiotest-145-serpent-128-xts-plain64-sha1 @@ -1503,7 +1491,6 @@ unlink TEST_DIR/luks-serpent-192-xts-plain64-sha1.img # ================= qemu-img serpent-192-xts-plain64-sha1 ================= # Create image qemu-img create -f luks --object secret,id=sec0,data=MTIzNDU2,format=base64 -o key-secret=sec0,iter-time=10,cipher-alg=serpent-192,cipher-mode=xts,ivgen-alg=plain64,hash-alg=sha1 TEST_DIR/luks-serpent-192-xts-plain64-sha1.img 4194304M -Formatting 'TEST_DIR/luks-serpent-192-xts-plain64-sha1.img', fmt=luks size=4398046511104 key-secret=sec0 cipher-alg=serpent-192 cipher-mode=xts ivgen-alg=plain64 hash-alg=sha1 iter-time=10 # Open dev sudo cryptsetup -q -v luksOpen TEST_DIR/luks-serpent-192-xts-plain64-sha1.img qiotest-145-serpent-192-xts-plain64-sha1 @@ -1625,7 +1612,6 @@ unlink TEST_DIR/luks-aes-256-xts-plain64-sha224.img # ================= qemu-img aes-256-xts-plain64-sha224 ================= # Create image qemu-img create -f luks --object secret,id=sec0,data=MTIzNDU2,format=base64 -o key-secret=sec0,iter-time=10,cipher-alg=aes-256,cipher-mode=xts,ivgen-alg=plain64,hash-alg=sha224 TEST_DIR/luks-aes-256-xts-plain64-sha224.img 4194304M -Formatting 'TEST_DIR/luks-aes-256-xts-plain64-sha224.img', fmt=luks size=4398046511104 key-secret=sec0 cipher-alg=aes-256 cipher-mode=xts ivgen-alg=plain64 hash-alg=sha224 iter-time=10 # Open dev sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-sha224.img qiotest-145-aes-256-xts-plain64-sha224 @@ -1745,7 +1731,6 @@ unlink TEST_DIR/luks-aes-256-xts-plain64-sha256.img # ================= qemu-img aes-256-xts-plain64-sha256 ================= # Create image qemu-img create -f luks --object secret,id=sec0,data=MTIzNDU2,format=base64 -o key-secret=sec0,iter-time=10,cipher-alg=aes-256,cipher-mode=xts,ivgen-alg=plain64,hash-alg=sha256 TEST_DIR/luks-aes-256-xts-plain64-sha256.img 4194304M -Formatting 'TEST_DIR/luks-aes-256-xts-plain64-sha256.img', fmt=luks size=4398046511104 key-secret=sec0 cipher-alg=aes-256 cipher-mode=xts ivgen-alg=plain64 hash-alg=sha256 iter-time=10 # Open dev sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-sha256.img qiotest-145-aes-256-xts-plain64-sha256 @@ -1865,7 +1850,6 @@ unlink TEST_DIR/luks-aes-256-xts-plain64-sha384.img # ================= qemu-img aes-256-xts-plain64-sha384 ================= # Create image qemu-img create -f luks --object secret,id=sec0,data=MTIzNDU2,format=base64 -o key-secret=sec0,iter-time=10,cipher-alg=aes-256,cipher-mode=xts,ivgen-alg=plain64,hash-alg=sha384 TEST_DIR/luks-aes-256-xts-plain64-sha384.img 4194304M -Formatting 'TEST_DIR/luks-aes-256-xts-plain64-sha384.img', fmt=luks size=4398046511104 key-secret=sec0 cipher-alg=aes-256 cipher-mode=xts ivgen-alg=plain64 hash-alg=sha384 iter-time=10 # Open dev sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-sha384.img qiotest-145-aes-256-xts-plain64-sha384 @@ -1985,7 +1969,6 @@ unlink TEST_DIR/luks-aes-256-xts-plain64-sha512.img # ================= qemu-img aes-256-xts-plain64-sha512 ================= # Create image qemu-img create -f luks --object secret,id=sec0,data=MTIzNDU2,format=base64 -o key-secret=sec0,iter-time=10,cipher-alg=aes-256,cipher-mode=xts,ivgen-alg=plain64,hash-alg=sha512 TEST_DIR/luks-aes-256-xts-plain64-sha512.img 4194304M -Formatting 'TEST_DIR/luks-aes-256-xts-plain64-sha512.img', fmt=luks size=4398046511104 key-secret=sec0 cipher-alg=aes-256 cipher-mode=xts ivgen-alg=plain64 hash-alg=sha512 iter-time=10 # Open dev sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-sha512.img qiotest-145-aes-256-xts-plain64-sha512 @@ -2105,7 +2088,6 @@ unlink TEST_DIR/luks-aes-256-xts-plain64-ripemd160.img # ================= qemu-img aes-256-xts-plain64-ripemd160 ================= # Create image qemu-img create -f luks --object secret,id=sec0,data=MTIzNDU2,format=base64 -o key-secret=sec0,iter-time=10,cipher-alg=aes-256,cipher-mode=xts,ivgen-alg=plain64,hash-alg=ripemd160 TEST_DIR/luks-aes-256-xts-plain64-ripemd160.img 4194304M -Formatting 'TEST_DIR/luks-aes-256-xts-plain64-ripemd160.img', fmt=luks size=4398046511104 key-secret=sec0 cipher-alg=aes-256 cipher-mode=xts ivgen-alg=plain64 hash-alg=ripemd160 iter-time=10 # Open dev sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-ripemd160.img qiotest-145-aes-256-xts-plain64-ripemd160 @@ -2299,7 +2281,6 @@ unlink TEST_DIR/luks-aes-256-xts-plain-sha1-pwallslots.img # ================= qemu-img aes-256-xts-plain-sha1-pwallslots ================= # Create image qemu-img create -f luks --object secret,id=sec0,data=c2xvdDE=,format=base64 -o key-secret=sec0,iter-time=10,cipher-alg=aes-256,cipher-mode=xts,ivgen-alg=plain,hash-alg=sha1 TEST_DIR/luks-aes-256-xts-plain-sha1-pwallslots.img 4194304M -Formatting 'TEST_DIR/luks-aes-256-xts-plain-sha1-pwallslots.img', fmt=luks size=4398046511104 key-secret=sec0 cipher-alg=aes-256 cipher-mode=xts ivgen-alg=plain hash-alg=sha1 iter-time=10 # Open dev sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain-sha1-pwallslots.img qiotest-145-aes-256-xts-plain-sha1-pwallslots @@ -2419,7 +2400,6 @@ unlink TEST_DIR/luks-aes-256-cbc-essiv-auto-sha1.img # ================= qemu-img aes-256-cbc-essiv-auto-sha1 ================= # Create image qemu-img create -f luks --object secret,id=sec0,data=MTIzNDU2,format=base64 -o key-secret=sec0,iter-time=10,cipher-alg=aes-256,cipher-mode=cbc,ivgen-alg=essiv,hash-alg=sha1 TEST_DIR/luks-aes-256-cbc-essiv-auto-sha1.img 4194304M -Formatting 'TEST_DIR/luks-aes-256-cbc-essiv-auto-sha1.img', fmt=luks size=4398046511104 key-secret=sec0 cipher-alg=aes-256 cipher-mode=cbc ivgen-alg=essiv hash-alg=sha1 iter-time=10 # Open dev sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-essiv-auto-sha1.img qiotest-145-aes-256-cbc-essiv-auto-sha1 @@ -2539,7 +2519,6 @@ unlink TEST_DIR/luks-aes-256-cbc-plain64-sha256-sha1.img # ================= qemu-img aes-256-cbc-plain64-sha256-sha1 ================= # Create image qemu-img create -f luks --object secret,id=sec0,data=MTIzNDU2,format=base64 -o key-secret=sec0,iter-time=10,cipher-alg=aes-256,cipher-mode=cbc,ivgen-alg=plain64,hash-alg=sha1,ivgen-hash-alg=sha256 TEST_DIR/luks-aes-256-cbc-plain64-sha256-sha1.img 4194304M -Formatting 'TEST_DIR/luks-aes-256-cbc-plain64-sha256-sha1.img', fmt=luks size=4398046511104 key-secret=sec0 cipher-alg=aes-256 cipher-mode=cbc ivgen-alg=plain64 ivgen-hash-alg=sha256 hash-alg=sha1 iter-time=10 # Open dev sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-plain64-sha256-sha1.img qiotest-145-aes-256-cbc-plain64-sha256-sha1 diff --git a/tests/qemu-iotests/237.out b/tests/qemu-iotests/237.out index 2f09ff5512..aeb9724492 100644 --- a/tests/qemu-iotests/237.out +++ b/tests/qemu-iotests/237.out @@ -129,11 +129,8 @@ Job failed: Cannot find device='this doesn't exist' nor node-name='this doesn't === Other subformats === -Formatting 'TEST_DIR/PID-t.vmdk.1', fmt=vmdk size=0 compat6=off hwversion=undefined -Formatting 'TEST_DIR/PID-t.vmdk.2', fmt=vmdk size=0 compat6=off hwversion=undefined -Formatting 'TEST_DIR/PID-t.vmdk.3', fmt=vmdk size=0 compat6=off hwversion=undefined == Missing extent == diff --git a/tests/qemu-iotests/255.out b/tests/qemu-iotests/255.out index 33b7f22de3..11a05a5213 100644 --- a/tests/qemu-iotests/255.out +++ b/tests/qemu-iotests/255.out @@ -3,9 +3,7 @@ Finishing a commit job with background reads === Create backing chain and start VM === -Formatting 'TEST_DIR/PID-t.qcow2.mid', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=134217728 lazy_refcounts=off refcount_bits=16 -Formatting 'TEST_DIR/PID-t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=134217728 lazy_refcounts=off refcount_bits=16 === Start background read requests === @@ -23,9 +21,7 @@ Closing the VM while a job is being cancelled === Create images and start VM === -Formatting 'TEST_DIR/PID-src.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=134217728 lazy_refcounts=off refcount_bits=16 -Formatting 'TEST_DIR/PID-dst.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=134217728 lazy_refcounts=off refcount_bits=16 wrote 1048576/1048576 bytes at offset 0 1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) diff --git a/tests/qemu-iotests/274.out b/tests/qemu-iotests/274.out index 16a95a4850..1d2928e14d 100644 --- a/tests/qemu-iotests/274.out +++ b/tests/qemu-iotests/274.out @@ -1,9 +1,6 @@ == Commit tests == -Formatting 'TEST_DIR/PID-base', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=2097152 lazy_refcounts=off refcount_bits=16 -Formatting 'TEST_DIR/PID-mid', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=1048576 backing_file=TEST_DIR/PID-base backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16 -Formatting 'TEST_DIR/PID-top', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=2097152 backing_file=TEST_DIR/PID-mid backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16 wrote 2097152/2097152 bytes at offset 0 2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -66,11 +63,8 @@ read 1048576/1048576 bytes at offset 1048576 1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) === Testing HMP commit (top -> mid) === -Formatting 'TEST_DIR/PID-base', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=2097152 lazy_refcounts=off refcount_bits=16 -Formatting 'TEST_DIR/PID-mid', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=1048576 backing_file=TEST_DIR/PID-base backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16 -Formatting 'TEST_DIR/PID-top', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=2097152 backing_file=TEST_DIR/PID-mid backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16 wrote 2097152/2097152 bytes at offset 0 2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -98,11 +92,8 @@ read 1048576/1048576 bytes at offset 1048576 1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) === Testing QMP active commit (top -> mid) === -Formatting 'TEST_DIR/PID-base', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=2097152 lazy_refcounts=off refcount_bits=16 -Formatting 'TEST_DIR/PID-mid', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=1048576 backing_file=TEST_DIR/PID-base backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16 -Formatting 'TEST_DIR/PID-top', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=2097152 backing_file=TEST_DIR/PID-mid backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16 wrote 2097152/2097152 bytes at offset 0 2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -136,11 +127,8 @@ read 1048576/1048576 bytes at offset 1048576 1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) === Testing qemu-img commit (top -> base) === -Formatting 'TEST_DIR/PID-base', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=2097152 lazy_refcounts=off refcount_bits=16 -Formatting 'TEST_DIR/PID-mid', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=1048576 backing_file=TEST_DIR/PID-base backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16 -Formatting 'TEST_DIR/PID-top', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=2097152 backing_file=TEST_DIR/PID-mid backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16 wrote 2097152/2097152 bytes at offset 0 2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -166,11 +154,8 @@ read 1048576/1048576 bytes at offset 1048576 1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) === Testing QMP active commit (top -> base) === -Formatting 'TEST_DIR/PID-base', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=2097152 lazy_refcounts=off refcount_bits=16 -Formatting 'TEST_DIR/PID-mid', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=1048576 backing_file=TEST_DIR/PID-base backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16 -Formatting 'TEST_DIR/PID-top', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=2097152 backing_file=TEST_DIR/PID-mid backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16 wrote 2097152/2097152 bytes at offset 0 2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -205,9 +190,7 @@ read 1048576/1048576 bytes at offset 1048576 == Resize tests == === preallocation=off === -Formatting 'TEST_DIR/PID-base', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=6442450944 lazy_refcounts=off refcount_bits=16 -Formatting 'TEST_DIR/PID-top', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=1073741824 backing_file=TEST_DIR/PID-base backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16 wrote 65536/65536 bytes at offset 5368709120 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -224,9 +207,7 @@ read 65536/65536 bytes at offset 5368709120 { "start": 1073741824, "length": 7516192768, "depth": 0, "present": true, "zero": true, "data": false}] === preallocation=metadata === -Formatting 'TEST_DIR/PID-base', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=34359738368 lazy_refcounts=off refcount_bits=16 -Formatting 'TEST_DIR/PID-top', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=32212254720 backing_file=TEST_DIR/PID-base backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16 wrote 65536/65536 bytes at offset 33285996544 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -248,9 +229,7 @@ read 65536/65536 bytes at offset 33285996544 { "start": 34896609280, "length": 536870912, "depth": 0, "present": true, "zero": true, "data": false, "offset": 2685075456}] === preallocation=falloc === -Formatting 'TEST_DIR/PID-base', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=10485760 lazy_refcounts=off refcount_bits=16 -Formatting 'TEST_DIR/PID-top', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=5242880 backing_file=TEST_DIR/PID-base backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16 wrote 65536/65536 bytes at offset 9437184 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -267,9 +246,7 @@ read 65536/65536 bytes at offset 9437184 { "start": 5242880, "length": 10485760, "depth": 0, "present": true, "zero": false, "data": true, "offset": 327680}] === preallocation=full === -Formatting 'TEST_DIR/PID-base', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=16777216 lazy_refcounts=off refcount_bits=16 -Formatting 'TEST_DIR/PID-top', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=8388608 backing_file=TEST_DIR/PID-base backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16 wrote 65536/65536 bytes at offset 11534336 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -286,9 +263,7 @@ read 65536/65536 bytes at offset 11534336 { "start": 8388608, "length": 4194304, "depth": 0, "present": true, "zero": false, "data": true, "offset": 327680}] === preallocation=off === -Formatting 'TEST_DIR/PID-base', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=393216 lazy_refcounts=off refcount_bits=16 -Formatting 'TEST_DIR/PID-top', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=259072 backing_file=TEST_DIR/PID-base backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16 wrote 65536/65536 bytes at offset 259072 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -306,9 +281,7 @@ read 65536/65536 bytes at offset 259072 { "start": 262144, "length": 262144, "depth": 0, "present": true, "zero": true, "data": false}] === preallocation=off === -Formatting 'TEST_DIR/PID-base', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=409600 lazy_refcounts=off refcount_bits=16 -Formatting 'TEST_DIR/PID-top', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=262144 backing_file=TEST_DIR/PID-base backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16 wrote 65536/65536 bytes at offset 344064 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -325,9 +298,7 @@ read 65536/65536 bytes at offset 344064 { "start": 262144, "length": 262144, "depth": 0, "present": true, "zero": true, "data": false}] === preallocation=off === -Formatting 'TEST_DIR/PID-base', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=524288 lazy_refcounts=off refcount_bits=16 -Formatting 'TEST_DIR/PID-top', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=262144 backing_file=TEST_DIR/PID-base backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16 wrote 65536/65536 bytes at offset 446464 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) diff --git a/tests/qemu-iotests/280.out b/tests/qemu-iotests/280.out index 09a0f1a7cb..e39164c579 100644 --- a/tests/qemu-iotests/280.out +++ b/tests/qemu-iotests/280.out @@ -1,4 +1,3 @@ -Formatting 'TEST_DIR/PID-base', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=67108864 lazy_refcounts=off refcount_bits=16 === Launch VM === Enabling migration QMP events on VM... diff --git a/tests/qemu-iotests/296.out b/tests/qemu-iotests/296.out index 6c69735604..42205cc981 100644 --- a/tests/qemu-iotests/296.out +++ b/tests/qemu-iotests/296.out @@ -1,4 +1,3 @@ -Formatting 'TEST_DIR/test.img', fmt=luks size=1048576 key-secret=keysec0 iter-time=10 {"execute": "job-dismiss", "arguments": {"id": "job0"}} {"return": {}} @@ -13,8 +12,7 @@ Job failed: Failed to get shared "consistent read" lock qemu-img: Failed to get shared "consistent read" lock Is another process using the image [TEST_DIR/test.img]? -.Formatting 'TEST_DIR/test.img', fmt=luks size=1048576 key-secret=keysec0 iter-time=10 - +. Job failed: Block node is read-only {"execute": "job-dismiss", "arguments": {"id": "job0"}} {"return": {}} @@ -26,12 +24,10 @@ Job failed: Failed to get shared "consistent read" lock {"return": {}} {"execute": "job-dismiss", "arguments": {"id": "job0"}} {"return": {}} -.Formatting 'TEST_DIR/test.img', fmt=luks size=1048576 key-secret=keysec0 iter-time=10 - +. {"return": {}} {"error": {"class": "GenericError", "desc": "Failed to get \"write\" lock"}} -.Formatting 'TEST_DIR/test.img', fmt=luks size=1048576 key-secret=keysec0 iter-time=10 - +. {"return": {}} {"return": {}} . diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py index c382c527c8..65780c6098 100644 --- a/tests/qemu-iotests/iotests.py +++ b/tests/qemu-iotests/iotests.py @@ -150,7 +150,9 @@ def qemu_tool_popen(args: Sequence[str], def qemu_tool_pipe_and_status(tool: str, args: Sequence[str], - connect_stderr: bool = True) -> Tuple[str, int]: + connect_stderr: bool = True, + drop_successful_output: bool = False) \ + -> Tuple[str, int]: """ Run a tool and return both its output and its exit code """ @@ -160,6 +162,8 @@ def qemu_tool_pipe_and_status(tool: str, args: Sequence[str], cmd = ' '.join(args) sys.stderr.write(f'{tool} received signal \ {-subp.returncode}: {cmd}\n') + if drop_successful_output and subp.returncode == 0: + output = '' return (output, subp.returncode) def qemu_img_create_prepare_args(args: List[str]) -> List[str]: @@ -204,8 +208,10 @@ def qemu_img_pipe_and_status(*args: str) -> Tuple[str, int]: """ Run qemu-img and return both its output and its exit code """ + is_create = bool(args and args[0] == 'create') full_args = qemu_img_args + qemu_img_create_prepare_args(list(args)) - return qemu_tool_pipe_and_status('qemu-img', full_args) + return qemu_tool_pipe_and_status('qemu-img', full_args, + drop_successful_output=is_create) def qemu_img(*args: str) -> int: '''Run qemu-img and return the exit code''' From e877bba308c38033f73f444395b3cdcde48a3393 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Thu, 23 Dec 2021 17:01:35 +0100 Subject: [PATCH 198/460] iotests.py: filter compression type out We want iotests pass with both the default zlib compression and with IMGOPTS='compression_type=zstd'. Actually the only test that is interested in real compression type in test output is 287 (test for qcow2 compression type) and it's in bash. So for now we can safely filter out compression type in all qcow2 tests. Signed-off-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Hanna Reitz Message-Id: <20211223160144.1097696-11-vsementsov@virtuozzo.com> Signed-off-by: Hanna Reitz --- tests/qemu-iotests/206.out | 10 +++++----- tests/qemu-iotests/242.out | 10 +++++----- tests/qemu-iotests/274.out | 10 +++++----- tests/qemu-iotests/iotests.py | 2 ++ 4 files changed, 17 insertions(+), 15 deletions(-) diff --git a/tests/qemu-iotests/206.out b/tests/qemu-iotests/206.out index 80cd274223..7e95694777 100644 --- a/tests/qemu-iotests/206.out +++ b/tests/qemu-iotests/206.out @@ -18,7 +18,7 @@ virtual size: 128 MiB (134217728 bytes) cluster_size: 65536 Format specific information: compat: 1.1 - compression type: zlib + compression type: COMPRESSION_TYPE lazy refcounts: false refcount bits: 16 corrupt: false @@ -42,7 +42,7 @@ virtual size: 64 MiB (67108864 bytes) cluster_size: 65536 Format specific information: compat: 1.1 - compression type: zlib + compression type: COMPRESSION_TYPE lazy refcounts: false refcount bits: 16 corrupt: false @@ -66,7 +66,7 @@ virtual size: 32 MiB (33554432 bytes) cluster_size: 2097152 Format specific information: compat: 1.1 - compression type: zlib + compression type: COMPRESSION_TYPE lazy refcounts: true refcount bits: 1 corrupt: false @@ -92,7 +92,7 @@ backing file: TEST_IMG.base backing file format: IMGFMT Format specific information: compat: 0.10 - compression type: zlib + compression type: COMPRESSION_TYPE refcount bits: 16 === Successful image creation (encrypted) === @@ -109,7 +109,7 @@ encrypted: yes cluster_size: 65536 Format specific information: compat: 1.1 - compression type: zlib + compression type: COMPRESSION_TYPE lazy refcounts: false refcount bits: 16 encrypt: diff --git a/tests/qemu-iotests/242.out b/tests/qemu-iotests/242.out index 3759c99284..ce231424a7 100644 --- a/tests/qemu-iotests/242.out +++ b/tests/qemu-iotests/242.out @@ -12,7 +12,7 @@ virtual size: 1 MiB (1048576 bytes) cluster_size: 65536 Format specific information: compat: 1.1 - compression type: zlib + compression type: COMPRESSION_TYPE lazy refcounts: false refcount bits: 16 corrupt: false @@ -34,7 +34,7 @@ virtual size: 1 MiB (1048576 bytes) cluster_size: 65536 Format specific information: compat: 1.1 - compression type: zlib + compression type: COMPRESSION_TYPE lazy refcounts: false bitmaps: [0]: @@ -68,7 +68,7 @@ virtual size: 1 MiB (1048576 bytes) cluster_size: 65536 Format specific information: compat: 1.1 - compression type: zlib + compression type: COMPRESSION_TYPE lazy refcounts: false bitmaps: [0]: @@ -110,7 +110,7 @@ virtual size: 1 MiB (1048576 bytes) cluster_size: 65536 Format specific information: compat: 1.1 - compression type: zlib + compression type: COMPRESSION_TYPE lazy refcounts: false bitmaps: [0]: @@ -161,7 +161,7 @@ virtual size: 1 MiB (1048576 bytes) cluster_size: 65536 Format specific information: compat: 1.1 - compression type: zlib + compression type: COMPRESSION_TYPE lazy refcounts: false bitmaps: [0]: diff --git a/tests/qemu-iotests/274.out b/tests/qemu-iotests/274.out index 1d2928e14d..1ce40d839a 100644 --- a/tests/qemu-iotests/274.out +++ b/tests/qemu-iotests/274.out @@ -50,7 +50,7 @@ backing file: TEST_DIR/PID-base backing file format: IMGFMT Format specific information: compat: 1.1 - compression type: zlib + compression type: COMPRESSION_TYPE lazy refcounts: false refcount bits: 16 corrupt: false @@ -79,7 +79,7 @@ backing file: TEST_DIR/PID-base backing file format: IMGFMT Format specific information: compat: 1.1 - compression type: zlib + compression type: COMPRESSION_TYPE lazy refcounts: false refcount bits: 16 corrupt: false @@ -114,7 +114,7 @@ backing file: TEST_DIR/PID-base backing file format: IMGFMT Format specific information: compat: 1.1 - compression type: zlib + compression type: COMPRESSION_TYPE lazy refcounts: false refcount bits: 16 corrupt: false @@ -141,7 +141,7 @@ virtual size: 2 MiB (2097152 bytes) cluster_size: 65536 Format specific information: compat: 1.1 - compression type: zlib + compression type: COMPRESSION_TYPE lazy refcounts: false refcount bits: 16 corrupt: false @@ -176,7 +176,7 @@ backing file: TEST_DIR/PID-base backing file format: IMGFMT Format specific information: compat: 1.1 - compression type: zlib + compression type: COMPRESSION_TYPE lazy refcounts: false refcount bits: 16 corrupt: false diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py index 65780c6098..8cdb381f2a 100644 --- a/tests/qemu-iotests/iotests.py +++ b/tests/qemu-iotests/iotests.py @@ -497,6 +497,8 @@ def filter_img_info(output, filename): 'uuid: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX', line) line = re.sub('cid: [0-9]+', 'cid: XXXXXXXXXX', line) + line = re.sub('(compression type: )(zlib|zstd)', r'\1COMPRESSION_TYPE', + line) lines.append(line) return '\n'.join(lines) From c30175d6fbc73a86a8013da195471d1a3490178f Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Thu, 23 Dec 2021 17:01:36 +0100 Subject: [PATCH 199/460] iotest 302: use img_info_log() helper Instead of qemu_img_log("info", ..) use generic helper img_info_log(). img_info_log() has smarter logic. For example it use filter_img_info() to filter output, which in turns filter a compression type. So it will help us in future when we implement a possibility to use zstd compression by default (with help of some runtime config file or maybe build option). For now to test you should recompile qemu with a small addition into block/qcow2.c before "if (qcow2_opts->has_compression_type": if (!qcow2_opts->has_compression_type && version >= 3) { qcow2_opts->has_compression_type = true; qcow2_opts->compression_type = QCOW2_COMPRESSION_TYPE_ZSTD; } Signed-off-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Max Reitz Message-Id: <20211223160144.1097696-12-vsementsov@virtuozzo.com> Signed-off-by: Hanna Reitz --- tests/qemu-iotests/302 | 4 +++- tests/qemu-iotests/302.out | 7 +++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/tests/qemu-iotests/302 b/tests/qemu-iotests/302 index 5695af4914..a6d79e727b 100755 --- a/tests/qemu-iotests/302 +++ b/tests/qemu-iotests/302 @@ -34,6 +34,7 @@ from iotests import ( qemu_img_measure, qemu_io, qemu_nbd_popen, + img_info_log, ) iotests.script_initialize(supported_fmts=["qcow2"]) @@ -88,6 +89,7 @@ with tarfile.open(tar_file, "w") as tar: tar_file): iotests.log("=== Target image info ===") + # Not img_info_log as it enforces imgfmt, but now we print info on raw qemu_img_log("info", nbd_uri) qemu_img( @@ -99,7 +101,7 @@ with tarfile.open(tar_file, "w") as tar: nbd_uri) iotests.log("=== Converted image info ===") - qemu_img_log("info", nbd_uri) + img_info_log(nbd_uri) iotests.log("=== Converted image check ===") qemu_img_log("check", nbd_uri) diff --git a/tests/qemu-iotests/302.out b/tests/qemu-iotests/302.out index e2f6077e83..3e7c281b91 100644 --- a/tests/qemu-iotests/302.out +++ b/tests/qemu-iotests/302.out @@ -6,14 +6,13 @@ virtual size: 448 KiB (458752 bytes) disk size: unavailable === Converted image info === -image: nbd+unix:///exp?socket=SOCK_DIR/PID-nbd-sock -file format: qcow2 +image: TEST_IMG +file format: IMGFMT virtual size: 1 GiB (1073741824 bytes) -disk size: unavailable cluster_size: 65536 Format specific information: compat: 1.1 - compression type: zlib + compression type: COMPRESSION_TYPE lazy refcounts: false refcount bits: 16 corrupt: false From 083c24561a1f52829b5b31a0fb2f7c77efb979c0 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Thu, 23 Dec 2021 17:01:37 +0100 Subject: [PATCH 200/460] qcow2: simple case support for downgrading of qcow2 images with zstd If image doesn't have any compressed cluster we can easily switch to zlib compression, which may allow to downgrade the image. That's mostly needed to support IMGOPTS='compression_type=zstd' in some iotests which do qcow2 downgrade. While being here also fix checkpatch complain against '#' in printf formatting. Signed-off-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Max Reitz Message-Id: <20211223160144.1097696-13-vsementsov@virtuozzo.com> Signed-off-by: Hanna Reitz --- block/qcow2.c | 58 +++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 56 insertions(+), 2 deletions(-) diff --git a/block/qcow2.c b/block/qcow2.c index d509016756..c8115e1cba 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -5279,6 +5279,38 @@ static int qcow2_load_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, return bs->drv->bdrv_co_preadv_part(bs, offset, qiov->size, qiov, 0, 0); } +static int qcow2_has_compressed_clusters(BlockDriverState *bs) +{ + int64_t offset = 0; + int64_t bytes = bdrv_getlength(bs); + + if (bytes < 0) { + return bytes; + } + + while (bytes != 0) { + int ret; + QCow2SubclusterType type; + unsigned int cur_bytes = MIN(INT_MAX, bytes); + uint64_t host_offset; + + ret = qcow2_get_host_offset(bs, offset, &cur_bytes, &host_offset, + &type); + if (ret < 0) { + return ret; + } + + if (type == QCOW2_SUBCLUSTER_COMPRESSED) { + return 1; + } + + offset += cur_bytes; + bytes -= cur_bytes; + } + + return 0; +} + /* * Downgrades an image's version. To achieve this, any incompatible features * have to be removed. @@ -5336,9 +5368,10 @@ static int qcow2_downgrade(BlockDriverState *bs, int target_version, * the first place; if that happens nonetheless, returning -ENOTSUP is the * best thing to do anyway */ - if (s->incompatible_features) { + if (s->incompatible_features & ~QCOW2_INCOMPAT_COMPRESSION) { error_setg(errp, "Cannot downgrade an image with incompatible features " - "%#" PRIx64 " set", s->incompatible_features); + "0x%" PRIx64 " set", + s->incompatible_features & ~QCOW2_INCOMPAT_COMPRESSION); return -ENOTSUP; } @@ -5356,6 +5389,27 @@ static int qcow2_downgrade(BlockDriverState *bs, int target_version, return ret; } + if (s->incompatible_features & QCOW2_INCOMPAT_COMPRESSION) { + ret = qcow2_has_compressed_clusters(bs); + if (ret < 0) { + error_setg(errp, "Failed to check block status"); + return -EINVAL; + } + if (ret) { + error_setg(errp, "Cannot downgrade an image with zstd compression " + "type and existing compressed clusters"); + return -ENOTSUP; + } + /* + * No compressed clusters for now, so just chose default zlib + * compression. + */ + s->incompatible_features &= ~QCOW2_INCOMPAT_COMPRESSION; + s->compression_type = QCOW2_COMPRESSION_TYPE_ZLIB; + } + + assert(s->incompatible_features == 0); + s->qcow_version = target_version; ret = qcow2_update_header(bs); if (ret < 0) { From c5e627a6ecdccea64a1b600857ed671a83377847 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Thu, 23 Dec 2021 17:01:38 +0100 Subject: [PATCH 201/460] iotests/common.rc: introduce _qcow2_dump_header helper We'll use it in tests instead of explicit qcow2.py. Then we are going to add some filtering in _qcow2_dump_header. Signed-off-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Max Reitz Message-Id: <20211223160144.1097696-14-vsementsov@virtuozzo.com> Signed-off-by: Hanna Reitz --- tests/qemu-iotests/common.rc | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc index d8582454de..5dea310ea0 100644 --- a/tests/qemu-iotests/common.rc +++ b/tests/qemu-iotests/common.rc @@ -996,5 +996,15 @@ _require_one_device_of() _notrun "$* not available" } +_qcow2_dump_header() +{ + img="$1" + if [ -z "$img" ]; then + img="$TEST_IMG" + fi + + $PYTHON qcow2.py "$img" dump-header +} + # make sure this script returns success true From 984d7a52d5ca33a79e09f2617fe43e368dce4068 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Thu, 23 Dec 2021 17:01:39 +0100 Subject: [PATCH 202/460] iotests: massive use _qcow2_dump_header We are going to add filtering in _qcow2_dump_header and want all tests use it. The patch is generated by commands: cd tests/qemu-iotests sed -ie 's/$PYTHON qcow2.py "$TEST_IMG" dump-header\($\| \)/_qcow2_dump_header\1/' ??? tests/* (the difficulty is to avoid converting dump-header-exts) Signed-off-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Max Reitz Message-Id: <20211223160144.1097696-15-vsementsov@virtuozzo.com> Signed-off-by: Hanna Reitz --- tests/qemu-iotests/031 | 6 +++--- tests/qemu-iotests/036 | 6 +++--- tests/qemu-iotests/039 | 20 ++++++++++---------- tests/qemu-iotests/060 | 20 ++++++++++---------- tests/qemu-iotests/061 | 36 ++++++++++++++++++------------------ tests/qemu-iotests/137 | 2 +- tests/qemu-iotests/287 | 8 ++++---- 7 files changed, 49 insertions(+), 49 deletions(-) diff --git a/tests/qemu-iotests/031 b/tests/qemu-iotests/031 index 58b57a0ef2..648112f796 100755 --- a/tests/qemu-iotests/031 +++ b/tests/qemu-iotests/031 @@ -58,21 +58,21 @@ for compat in "compat=0.10" "compat=1.1"; do echo _make_test_img -o $compat 64M $PYTHON qcow2.py "$TEST_IMG" add-header-ext 0x12345678 "This is a test header extension" - $PYTHON qcow2.py "$TEST_IMG" dump-header + _qcow2_dump_header _check_test_img echo echo === Rewrite header with no backing file === echo $QEMU_IMG rebase -u -b "" "$TEST_IMG" - $PYTHON qcow2.py "$TEST_IMG" dump-header + _qcow2_dump_header _check_test_img echo echo === Add a backing file and format === echo $QEMU_IMG rebase -u -b "/some/backing/file/path" -F host_device "$TEST_IMG" - $PYTHON qcow2.py "$TEST_IMG" dump-header + _qcow2_dump_header done # success, all done diff --git a/tests/qemu-iotests/036 b/tests/qemu-iotests/036 index 5e567012a8..f703605e44 100755 --- a/tests/qemu-iotests/036 +++ b/tests/qemu-iotests/036 @@ -58,7 +58,7 @@ $PYTHON qcow2.py "$TEST_IMG" set-feature-bit incompatible 63 # Without feature table $PYTHON qcow2.py "$TEST_IMG" del-header-ext 0x6803f857 -$PYTHON qcow2.py "$TEST_IMG" dump-header | grep features +_qcow2_dump_header | grep features $PYTHON qcow2.py "$TEST_IMG" dump-header-exts _img_info @@ -107,7 +107,7 @@ echo === Create image with unknown autoclear feature bit === echo _make_test_img 64M $PYTHON qcow2.py "$TEST_IMG" set-feature-bit autoclear 63 -$PYTHON qcow2.py "$TEST_IMG" dump-header | grep features +_qcow2_dump_header | grep features $PYTHON qcow2.py "$TEST_IMG" dump-header-exts echo @@ -115,7 +115,7 @@ echo === Repair image === echo _check_test_img -r all -$PYTHON qcow2.py "$TEST_IMG" dump-header | grep features +_qcow2_dump_header | grep features $PYTHON qcow2.py "$TEST_IMG" dump-header-exts # success, all done diff --git a/tests/qemu-iotests/039 b/tests/qemu-iotests/039 index 12b2c7fa7b..8e783a8380 100755 --- a/tests/qemu-iotests/039 +++ b/tests/qemu-iotests/039 @@ -59,7 +59,7 @@ _make_test_img -o "compat=1.1,lazy_refcounts=on" $size $QEMU_IO -c "write -P 0x5a 0 512" "$TEST_IMG" | _filter_qemu_io # The dirty bit must not be set -$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features +_qcow2_dump_header | grep incompatible_features _check_test_img echo @@ -73,7 +73,7 @@ $QEMU_IO -c "write -P 0x5a 0 512" \ | _filter_qemu_io # The dirty bit must be set -$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features +_qcow2_dump_header | grep incompatible_features _check_test_img echo @@ -82,7 +82,7 @@ echo "== Read-only access must still work ==" $QEMU_IO -r -c "read -P 0x5a 0 512" "$TEST_IMG" | _filter_qemu_io # The dirty bit must be set -$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features +_qcow2_dump_header | grep incompatible_features echo echo "== Repairing the image file must succeed ==" @@ -90,7 +90,7 @@ echo "== Repairing the image file must succeed ==" _check_test_img -r all # The dirty bit must not be set -$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features +_qcow2_dump_header | grep incompatible_features echo echo "== Data should still be accessible after repair ==" @@ -108,12 +108,12 @@ $QEMU_IO -c "write -P 0x5a 0 512" \ | _filter_qemu_io # The dirty bit must be set -$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features +_qcow2_dump_header | grep incompatible_features $QEMU_IO -c "write 0 512" "$TEST_IMG" | _filter_qemu_io # The dirty bit must not be set -$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features +_qcow2_dump_header | grep incompatible_features echo echo "== Creating an image file with lazy_refcounts=off ==" @@ -126,7 +126,7 @@ $QEMU_IO -c "write -P 0x5a 0 512" \ | _filter_qemu_io # The dirty bit must not be set since lazy_refcounts=off -$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features +_qcow2_dump_header | grep incompatible_features _check_test_img echo @@ -141,7 +141,7 @@ $QEMU_IO -c "write 0 512" "$TEST_IMG" | _filter_qemu_io $QEMU_IMG commit "$TEST_IMG" # The dirty bit must not be set -$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features +_qcow2_dump_header | grep incompatible_features $PYTHON qcow2.py "$TEST_IMG".base dump-header | grep incompatible_features _check_test_img @@ -159,7 +159,7 @@ $QEMU_IO -c "reopen -o lazy-refcounts=on" \ | _filter_qemu_io # The dirty bit must be set -$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features +_qcow2_dump_header | grep incompatible_features _check_test_img _make_test_img -o "compat=1.1,lazy_refcounts=on" $size @@ -171,7 +171,7 @@ $QEMU_IO -c "reopen -o lazy-refcounts=off" \ | _filter_qemu_io # The dirty bit must not be set -$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features +_qcow2_dump_header | grep incompatible_features _check_test_img diff --git a/tests/qemu-iotests/060 b/tests/qemu-iotests/060 index db26c6b246..d1e3204d4e 100755 --- a/tests/qemu-iotests/060 +++ b/tests/qemu-iotests/060 @@ -80,13 +80,13 @@ poke_file "$TEST_IMG" "$l1_offset" "\x80\x00\x00\x00\x00\x03\x00\x00" _check_test_img # The corrupt bit should not be set anyway -$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features +_qcow2_dump_header | grep incompatible_features # Try to write something, thereby forcing the corrupt bit to be set $QEMU_IO -c "$OPEN_RW" -c "write -P 0x2a 0 512" | _filter_qemu_io # The corrupt bit must now be set -$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features +_qcow2_dump_header | grep incompatible_features # This information should be available through qemu-img info _img_info --format-specific @@ -114,19 +114,19 @@ poke_file "$TEST_IMG" "$(($rb_offset+8))" "\x00\x01" # Redirect new data cluster onto refcount block poke_file "$TEST_IMG" "$l2_offset" "\x80\x00\x00\x00\x00\x02\x00\x00" _check_test_img -$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features +_qcow2_dump_header | grep incompatible_features $QEMU_IO -c "$OPEN_RW" -c "write -P 0x2a 0 512" | _filter_qemu_io -$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features +_qcow2_dump_header | grep incompatible_features # Try to fix it _check_test_img -r all # The corrupt bit should be cleared -$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features +_qcow2_dump_header | grep incompatible_features # Look if it's really really fixed $QEMU_IO -c "$OPEN_RW" -c "write -P 0x2a 0 512" | _filter_qemu_io -$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features +_qcow2_dump_header | grep incompatible_features echo echo "=== Testing cluster data reference into inactive L2 table ===" @@ -139,13 +139,13 @@ $QEMU_IO -c "$OPEN_RW" -c "write -P 2 0 512" | _filter_qemu_io poke_file "$TEST_IMG" "$l2_offset_after_snapshot" \ "\x80\x00\x00\x00\x00\x04\x00\x00" _check_test_img -$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features +_qcow2_dump_header | grep incompatible_features $QEMU_IO -c "$OPEN_RW" -c "write -P 3 0 512" | _filter_qemu_io -$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features +_qcow2_dump_header | grep incompatible_features _check_test_img -r all -$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features +_qcow2_dump_header | grep incompatible_features $QEMU_IO -c "$OPEN_RW" -c "write -P 4 0 512" | _filter_qemu_io -$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features +_qcow2_dump_header | grep incompatible_features # Check data $QEMU_IO -c "$OPEN_RO" -c "read -P 4 0 512" | _filter_qemu_io diff --git a/tests/qemu-iotests/061 b/tests/qemu-iotests/061 index 9507c223bd..70edf1a163 100755 --- a/tests/qemu-iotests/061 +++ b/tests/qemu-iotests/061 @@ -55,9 +55,9 @@ echo "=== Testing version downgrade with zero expansion ===" echo _make_test_img -o "compat=1.1,lazy_refcounts=on" 64M $QEMU_IO -c "write -z 0 128k" "$TEST_IMG" | _filter_qemu_io -$PYTHON qcow2.py "$TEST_IMG" dump-header +_qcow2_dump_header $QEMU_IMG amend -o "compat=0.10" "$TEST_IMG" -$PYTHON qcow2.py "$TEST_IMG" dump-header +_qcow2_dump_header $QEMU_IO -c "read -P 0 0 128k" "$TEST_IMG" | _filter_qemu_io _check_test_img @@ -68,10 +68,10 @@ _make_test_img -o "compat=1.1,lazy_refcounts=on" 64M $QEMU_IO -c "write -z 0 128k" "$TEST_IMG" | _filter_qemu_io $QEMU_IO -c "write -z 32M 128k" "$TEST_IMG" | _filter_qemu_io $QEMU_IO -c map "$TEST_IMG" | _filter_qemu_io -$PYTHON qcow2.py "$TEST_IMG" dump-header +_qcow2_dump_header $QEMU_IMG amend -o "compat=0.10" --image-opts \ driver=qcow2,file.filename=$TEST_IMG,l2-cache-entry-size=4096 -$PYTHON qcow2.py "$TEST_IMG" dump-header +_qcow2_dump_header $QEMU_IO -c "read -P 0 0 128k" "$TEST_IMG" | _filter_qemu_io $QEMU_IO -c "read -P 0 32M 128k" "$TEST_IMG" | _filter_qemu_io $QEMU_IO -c map "$TEST_IMG" | _filter_qemu_io @@ -84,9 +84,9 @@ _make_test_img -o "compat=1.1,lazy_refcounts=on" 64M _NO_VALGRIND \ $QEMU_IO -c "write -P 0x2a 0 128k" -c flush \ -c "sigraise $(kill -l KILL)" "$TEST_IMG" 2>&1 | _filter_qemu_io -$PYTHON qcow2.py "$TEST_IMG" dump-header +_qcow2_dump_header $QEMU_IMG amend -o "compat=0.10" "$TEST_IMG" -$PYTHON qcow2.py "$TEST_IMG" dump-header +_qcow2_dump_header $QEMU_IO -c "read -P 0x2a 0 128k" "$TEST_IMG" | _filter_qemu_io _check_test_img @@ -96,9 +96,9 @@ echo _make_test_img -o "compat=1.1" 64M $PYTHON qcow2.py "$TEST_IMG" set-feature-bit compatible 42 $PYTHON qcow2.py "$TEST_IMG" set-feature-bit autoclear 42 -$PYTHON qcow2.py "$TEST_IMG" dump-header +_qcow2_dump_header $QEMU_IMG amend -o "compat=0.10" "$TEST_IMG" -$PYTHON qcow2.py "$TEST_IMG" dump-header +_qcow2_dump_header _check_test_img echo @@ -106,9 +106,9 @@ echo "=== Testing version upgrade and resize ===" echo _make_test_img -o "compat=0.10" 64M $QEMU_IO -c "write -P 0x2a 42M 64k" "$TEST_IMG" | _filter_qemu_io -$PYTHON qcow2.py "$TEST_IMG" dump-header +_qcow2_dump_header $QEMU_IMG amend -o "compat=1.1,lazy_refcounts=on,size=128M" "$TEST_IMG" -$PYTHON qcow2.py "$TEST_IMG" dump-header +_qcow2_dump_header $QEMU_IO -c "read -P 0x2a 42M 64k" "$TEST_IMG" | _filter_qemu_io _check_test_img @@ -120,29 +120,29 @@ $QEMU_IO -c "write -P 0x2a 24M 64k" "$TEST_IMG" | _filter_qemu_io $QEMU_IMG snapshot -c foo "$TEST_IMG" $QEMU_IMG resize "$TEST_IMG" 64M && echo "unexpected pass" -$PYTHON qcow2.py "$TEST_IMG" dump-header | grep '^\(version\|size\|nb_snap\)' +_qcow2_dump_header | grep '^\(version\|size\|nb_snap\)' $QEMU_IMG amend -o "compat=1.1,size=128M" "$TEST_IMG" || echo "unexpected fail" -$PYTHON qcow2.py "$TEST_IMG" dump-header | grep '^\(version\|size\|nb_snap\)' +_qcow2_dump_header | grep '^\(version\|size\|nb_snap\)' $QEMU_IMG snapshot -c bar "$TEST_IMG" $QEMU_IMG resize --shrink "$TEST_IMG" 64M || echo "unexpected fail" -$PYTHON qcow2.py "$TEST_IMG" dump-header | grep '^\(version\|size\|nb_snap\)' +_qcow2_dump_header | grep '^\(version\|size\|nb_snap\)' $QEMU_IMG amend -o "compat=0.10,size=32M" "$TEST_IMG" && echo "unexpected pass" -$PYTHON qcow2.py "$TEST_IMG" dump-header | grep '^\(version\|size\|nb_snap\)' +_qcow2_dump_header | grep '^\(version\|size\|nb_snap\)' $QEMU_IMG snapshot -a bar "$TEST_IMG" || echo "unexpected fail" -$PYTHON qcow2.py "$TEST_IMG" dump-header | grep '^\(version\|size\|nb_snap\)' +_qcow2_dump_header | grep '^\(version\|size\|nb_snap\)' $QEMU_IMG snapshot -d bar "$TEST_IMG" $QEMU_IMG amend -o "compat=0.10,size=32M" "$TEST_IMG" || echo "unexpected fail" -$PYTHON qcow2.py "$TEST_IMG" dump-header | grep '^\(version\|size\|nb_snap\)' +_qcow2_dump_header | grep '^\(version\|size\|nb_snap\)' _check_test_img @@ -154,9 +154,9 @@ _make_test_img -o "compat=1.1,lazy_refcounts=on" 64M _NO_VALGRIND \ $QEMU_IO -c "write -P 0x2a 0 128k" -c flush \ -c "sigraise $(kill -l KILL)" "$TEST_IMG" 2>&1 | _filter_qemu_io -$PYTHON qcow2.py "$TEST_IMG" dump-header +_qcow2_dump_header $QEMU_IMG amend -o "lazy_refcounts=off" "$TEST_IMG" -$PYTHON qcow2.py "$TEST_IMG" dump-header +_qcow2_dump_header $QEMU_IO -c "read -P 0x2a 0 128k" "$TEST_IMG" | _filter_qemu_io _check_test_img diff --git a/tests/qemu-iotests/137 b/tests/qemu-iotests/137 index 4680d5df3d..52ee135184 100755 --- a/tests/qemu-iotests/137 +++ b/tests/qemu-iotests/137 @@ -140,7 +140,7 @@ $QEMU_IO \ # The dirty bit must not be set # (Filter the external data file bit) -if $PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features \ +if _qcow2_dump_header | grep incompatible_features \ | grep -q '\<0\>' then echo 'ERROR: Dirty bit set' diff --git a/tests/qemu-iotests/287 b/tests/qemu-iotests/287 index 2d5334e8bf..5427ad5456 100755 --- a/tests/qemu-iotests/287 +++ b/tests/qemu-iotests/287 @@ -61,13 +61,13 @@ echo echo "=== Testing compression type incompatible bit setting for zlib ===" echo _make_test_img -o compression_type=zlib 64M -$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features +_qcow2_dump_header | grep incompatible_features echo echo "=== Testing compression type incompatible bit setting for zstd ===" echo _make_test_img -o compression_type=zstd 64M -$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features +_qcow2_dump_header | grep incompatible_features echo echo "=== Testing zlib with incompatible bit set ===" @@ -75,7 +75,7 @@ echo _make_test_img -o compression_type=zlib 64M $PYTHON qcow2.py "$TEST_IMG" set-feature-bit incompatible 3 # to make sure the bit was actually set -$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features +_qcow2_dump_header | grep incompatible_features if $QEMU_IMG info "$TEST_IMG" >/dev/null 2>&1 ; then echo "Error: The image opened successfully. The image must not be opened." @@ -87,7 +87,7 @@ echo _make_test_img -o compression_type=zstd 64M $PYTHON qcow2.py "$TEST_IMG" set-header incompatible_features 0 # to make sure the bit was actually unset -$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features +_qcow2_dump_header | grep incompatible_features if $QEMU_IMG info "$TEST_IMG" >/dev/null 2>&1 ; then echo "Error: The image opened successfully. The image must not be opened." From 72be51ddb3d10974da5128ad20ee5ca7a13ddd54 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Thu, 23 Dec 2021 17:01:40 +0100 Subject: [PATCH 203/460] iotest 39: use _qcow2_dump_header _qcow2_dump_header has filter for compression type, so this change makes test pass with IMGOPTS='compression_type=zstd'. Signed-off-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Max Reitz Message-Id: <20211223160144.1097696-16-vsementsov@virtuozzo.com> Signed-off-by: Hanna Reitz --- tests/qemu-iotests/039 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/qemu-iotests/039 b/tests/qemu-iotests/039 index 8e783a8380..00d379cde2 100755 --- a/tests/qemu-iotests/039 +++ b/tests/qemu-iotests/039 @@ -142,7 +142,7 @@ $QEMU_IMG commit "$TEST_IMG" # The dirty bit must not be set _qcow2_dump_header | grep incompatible_features -$PYTHON qcow2.py "$TEST_IMG".base dump-header | grep incompatible_features +_qcow2_dump_header "$TEST_IMG".base | grep incompatible_features _check_test_img TEST_IMG="$TEST_IMG".base _check_test_img From dba5aee4da9ce0a81e5a870c22a19926212dc87e Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Thu, 23 Dec 2021 17:01:41 +0100 Subject: [PATCH 204/460] iotests: bash tests: filter compression type We want iotests pass with both the default zlib compression and with IMGOPTS='compression_type=zstd'. Actually the only test that is interested in real compression type in test output is 287 (test for qcow2 compression type), so implement specific option for it. Signed-off-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Hanna Reitz Message-Id: <20211223160144.1097696-17-vsementsov@virtuozzo.com> Signed-off-by: Hanna Reitz --- tests/qemu-iotests/060.out | 2 +- tests/qemu-iotests/061.out | 12 ++++++------ tests/qemu-iotests/082.out | 14 +++++++------- tests/qemu-iotests/198.out | 4 ++-- tests/qemu-iotests/287 | 8 ++++---- tests/qemu-iotests/common.filter | 8 ++++++++ tests/qemu-iotests/common.rc | 14 +++++++++++++- 7 files changed, 41 insertions(+), 21 deletions(-) diff --git a/tests/qemu-iotests/060.out b/tests/qemu-iotests/060.out index b74540bafb..329977d9b9 100644 --- a/tests/qemu-iotests/060.out +++ b/tests/qemu-iotests/060.out @@ -17,7 +17,7 @@ virtual size: 64 MiB (67108864 bytes) cluster_size: 65536 Format specific information: compat: 1.1 - compression type: zlib + compression type: COMPRESSION_TYPE lazy refcounts: false refcount bits: 16 corrupt: true diff --git a/tests/qemu-iotests/061.out b/tests/qemu-iotests/061.out index 7ecbd4dea8..139fc68177 100644 --- a/tests/qemu-iotests/061.out +++ b/tests/qemu-iotests/061.out @@ -525,7 +525,7 @@ virtual size: 64 MiB (67108864 bytes) cluster_size: 65536 Format specific information: compat: 1.1 - compression type: zlib + compression type: COMPRESSION_TYPE lazy refcounts: false refcount bits: 16 data file: TEST_DIR/t.IMGFMT.data @@ -552,7 +552,7 @@ virtual size: 64 MiB (67108864 bytes) cluster_size: 65536 Format specific information: compat: 1.1 - compression type: zlib + compression type: COMPRESSION_TYPE lazy refcounts: false refcount bits: 16 data file: foo @@ -567,7 +567,7 @@ virtual size: 64 MiB (67108864 bytes) cluster_size: 65536 Format specific information: compat: 1.1 - compression type: zlib + compression type: COMPRESSION_TYPE lazy refcounts: false refcount bits: 16 data file raw: false @@ -583,7 +583,7 @@ virtual size: 64 MiB (67108864 bytes) cluster_size: 65536 Format specific information: compat: 1.1 - compression type: zlib + compression type: COMPRESSION_TYPE lazy refcounts: false refcount bits: 16 data file: TEST_DIR/t.IMGFMT.data @@ -597,7 +597,7 @@ virtual size: 64 MiB (67108864 bytes) cluster_size: 65536 Format specific information: compat: 1.1 - compression type: zlib + compression type: COMPRESSION_TYPE lazy refcounts: false refcount bits: 16 data file: TEST_DIR/t.IMGFMT.data @@ -612,7 +612,7 @@ virtual size: 64 MiB (67108864 bytes) cluster_size: 65536 Format specific information: compat: 1.1 - compression type: zlib + compression type: COMPRESSION_TYPE lazy refcounts: false refcount bits: 16 data file: TEST_DIR/t.IMGFMT.data diff --git a/tests/qemu-iotests/082.out b/tests/qemu-iotests/082.out index 077ed0f2c7..d0dd333117 100644 --- a/tests/qemu-iotests/082.out +++ b/tests/qemu-iotests/082.out @@ -17,7 +17,7 @@ virtual size: 128 MiB (134217728 bytes) cluster_size: 4096 Format specific information: compat: 1.1 - compression type: zlib + compression type: COMPRESSION_TYPE lazy refcounts: true refcount bits: 16 corrupt: false @@ -31,7 +31,7 @@ virtual size: 128 MiB (134217728 bytes) cluster_size: 8192 Format specific information: compat: 1.1 - compression type: zlib + compression type: COMPRESSION_TYPE lazy refcounts: true refcount bits: 16 corrupt: false @@ -329,7 +329,7 @@ virtual size: 128 MiB (134217728 bytes) cluster_size: 4096 Format specific information: compat: 1.1 - compression type: zlib + compression type: COMPRESSION_TYPE lazy refcounts: true refcount bits: 16 corrupt: false @@ -342,7 +342,7 @@ virtual size: 128 MiB (134217728 bytes) cluster_size: 8192 Format specific information: compat: 1.1 - compression type: zlib + compression type: COMPRESSION_TYPE lazy refcounts: true refcount bits: 16 corrupt: false @@ -639,7 +639,7 @@ virtual size: 128 MiB (134217728 bytes) cluster_size: 65536 Format specific information: compat: 1.1 - compression type: zlib + compression type: COMPRESSION_TYPE lazy refcounts: true refcount bits: 16 corrupt: false @@ -652,7 +652,7 @@ virtual size: 130 MiB (136314880 bytes) cluster_size: 65536 Format specific information: compat: 1.1 - compression type: zlib + compression type: COMPRESSION_TYPE lazy refcounts: false refcount bits: 16 corrupt: false @@ -665,7 +665,7 @@ virtual size: 132 MiB (138412032 bytes) cluster_size: 65536 Format specific information: compat: 1.1 - compression type: zlib + compression type: COMPRESSION_TYPE lazy refcounts: true refcount bits: 16 corrupt: false diff --git a/tests/qemu-iotests/198.out b/tests/qemu-iotests/198.out index 3952708444..805494916f 100644 --- a/tests/qemu-iotests/198.out +++ b/tests/qemu-iotests/198.out @@ -36,7 +36,7 @@ image: json:{ /* filtered */ } file format: IMGFMT virtual size: 16 MiB (16777216 bytes) Format specific information: - compression type: zlib + compression type: COMPRESSION_TYPE encrypt: ivgen alg: plain64 hash alg: sha256 @@ -81,7 +81,7 @@ virtual size: 16 MiB (16777216 bytes) backing file: TEST_DIR/t.IMGFMT.base backing file format: IMGFMT Format specific information: - compression type: zlib + compression type: COMPRESSION_TYPE encrypt: ivgen alg: plain64 hash alg: sha256 diff --git a/tests/qemu-iotests/287 b/tests/qemu-iotests/287 index 5427ad5456..6414640b21 100755 --- a/tests/qemu-iotests/287 +++ b/tests/qemu-iotests/287 @@ -61,13 +61,13 @@ echo echo "=== Testing compression type incompatible bit setting for zlib ===" echo _make_test_img -o compression_type=zlib 64M -_qcow2_dump_header | grep incompatible_features +_qcow2_dump_header --no-filter-compression | grep incompatible_features echo echo "=== Testing compression type incompatible bit setting for zstd ===" echo _make_test_img -o compression_type=zstd 64M -_qcow2_dump_header | grep incompatible_features +_qcow2_dump_header --no-filter-compression | grep incompatible_features echo echo "=== Testing zlib with incompatible bit set ===" @@ -75,7 +75,7 @@ echo _make_test_img -o compression_type=zlib 64M $PYTHON qcow2.py "$TEST_IMG" set-feature-bit incompatible 3 # to make sure the bit was actually set -_qcow2_dump_header | grep incompatible_features +_qcow2_dump_header --no-filter-compression | grep incompatible_features if $QEMU_IMG info "$TEST_IMG" >/dev/null 2>&1 ; then echo "Error: The image opened successfully. The image must not be opened." @@ -87,7 +87,7 @@ echo _make_test_img -o compression_type=zstd 64M $PYTHON qcow2.py "$TEST_IMG" set-header incompatible_features 0 # to make sure the bit was actually unset -_qcow2_dump_header | grep incompatible_features +_qcow2_dump_header --no-filter-compression | grep incompatible_features if $QEMU_IMG info "$TEST_IMG" >/dev/null 2>&1 ; then echo "Error: The image opened successfully. The image must not be opened." diff --git a/tests/qemu-iotests/common.filter b/tests/qemu-iotests/common.filter index 2b2b53946c..75cc241580 100644 --- a/tests/qemu-iotests/common.filter +++ b/tests/qemu-iotests/common.filter @@ -247,6 +247,7 @@ _filter_img_info() -e "/block_state_zero: \\(on\\|off\\)/d" \ -e "/log_size: [0-9]\\+/d" \ -e "s/iters: [0-9]\\+/iters: 1024/" \ + -e 's/\(compression type: \)\(zlib\|zstd\)/\1COMPRESSION_TYPE/' \ -e "s/uuid: [-a-f0-9]\\+/uuid: 00000000-0000-0000-0000-000000000000/" | \ while IFS='' read -r line; do if [[ $format_specific == 1 ]]; then @@ -337,5 +338,12 @@ _filter_authz_check_tls() $SED -e 's/TLS x509 authz check for .* is denied/TLS x509 authz check for DISTINGUISHED-NAME is denied/' } +_filter_qcow2_compression_type_bit() +{ + $SED -e 's/\(incompatible_features\s\+\)\[3\(, \)\?/\1[/' \ + -e 's/\(incompatible_features.*\), 3\]/\1]/' \ + -e 's/\(incompatible_features.*\), 3\(,.*\)/\1\2/' +} + # make sure this script returns success true diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc index 5dea310ea0..9885030b43 100644 --- a/tests/qemu-iotests/common.rc +++ b/tests/qemu-iotests/common.rc @@ -699,6 +699,7 @@ _img_info() -e "s#$TEST_DIR#TEST_DIR#g" \ -e "s#$SOCK_DIR/fuse-#TEST_DIR/#g" \ -e "s#$IMGFMT#IMGFMT#g" \ + -e 's/\(compression type: \)\(zlib\|zstd\)/\1COMPRESSION_TYPE/' \ -e "/^disk size:/ D" \ -e "/actual-size/ D" | \ while IFS='' read -r line; do @@ -998,12 +999,23 @@ _require_one_device_of() _qcow2_dump_header() { + if [[ "$1" == "--no-filter-compression" ]]; then + local filter_compression=0 + shift + else + local filter_compression=1 + fi + img="$1" if [ -z "$img" ]; then img="$TEST_IMG" fi - $PYTHON qcow2.py "$img" dump-header + if [[ $filter_compression == 0 ]]; then + $PYTHON qcow2.py "$img" dump-header + else + $PYTHON qcow2.py "$img" dump-header | _filter_qcow2_compression_type_bit + fi } # make sure this script returns success From 3a0e60a065ee32027684314a0d886d43bc6ce34b Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Thu, 23 Dec 2021 17:01:42 +0100 Subject: [PATCH 205/460] iotests 60: more accurate set dirty bit in qcow2 header Don't touch other incompatible bits, like compression-type. This makes the test pass with IMGOPTS='compression_type=zstd'. Signed-off-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Max Reitz Message-Id: <20211223160144.1097696-18-vsementsov@virtuozzo.com> Signed-off-by: Hanna Reitz --- tests/qemu-iotests/060 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/qemu-iotests/060 b/tests/qemu-iotests/060 index d1e3204d4e..df87d600f7 100755 --- a/tests/qemu-iotests/060 +++ b/tests/qemu-iotests/060 @@ -326,7 +326,7 @@ _make_test_img 64M # Let the refblock appear unaligned poke_file "$TEST_IMG" "$rt_offset" "\x00\x00\x00\x00\xff\xff\x2a\x00" # Mark the image dirty, thus forcing an automatic check when opening it -poke_file "$TEST_IMG" 72 "\x00\x00\x00\x00\x00\x00\x00\x01" +$PYTHON qcow2.py "$TEST_IMG" set-feature-bit incompatible 0 # Open the image (qemu should refuse to do so) $QEMU_IO -c close "$TEST_IMG" 2>&1 | _filter_testdir | _filter_imgfmt From da87d5f83a23dd9b252fabc7787383ce6d2454a3 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Thu, 23 Dec 2021 17:01:43 +0100 Subject: [PATCH 206/460] iotest 214: explicit compression type The test-case "Corrupted size field in compressed cluster descriptor" heavily depends on zlib compression type. So, make it explicit. This way test passes with IMGOPTS='compression_type=zstd'. Signed-off-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Max Reitz Message-Id: <20211223160144.1097696-19-vsementsov@virtuozzo.com> Signed-off-by: Hanna Reitz --- tests/qemu-iotests/214 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/qemu-iotests/214 b/tests/qemu-iotests/214 index 0889089d81..c66e246ba2 100755 --- a/tests/qemu-iotests/214 +++ b/tests/qemu-iotests/214 @@ -51,7 +51,7 @@ echo # The L2 entries of the two compressed clusters are located at # 0x800000 and 0x800008, their original values are 0x4008000000a00000 # and 0x4008000000a00802 (5 sectors for compressed data each). -_make_test_img 8M -o cluster_size=2M +_make_test_img 8M -o cluster_size=2M,compression_type=zlib $QEMU_IO -c "write -c -P 0x11 0 2M" -c "write -c -P 0x11 2M 2M" "$TEST_IMG" \ 2>&1 | _filter_qemu_io | _filter_testdir From e287a351db13f3f6670f4a3f2896f2dde47d07d1 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Thu, 23 Dec 2021 17:01:44 +0100 Subject: [PATCH 207/460] iotests: declare lack of support for compresion_type in IMGOPTS compression_type can't be used if we want to create image with compat=0.10. So, skip these tests, not many of them. Signed-off-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Hanna Reitz Message-Id: <20211223160144.1097696-20-vsementsov@virtuozzo.com> Signed-off-by: Hanna Reitz --- tests/qemu-iotests/031 | 5 +++-- tests/qemu-iotests/051 | 5 +++-- tests/qemu-iotests/061 | 6 +++++- tests/qemu-iotests/112 | 3 ++- tests/qemu-iotests/290 | 2 +- 5 files changed, 14 insertions(+), 7 deletions(-) diff --git a/tests/qemu-iotests/031 b/tests/qemu-iotests/031 index 648112f796..ee587b1606 100755 --- a/tests/qemu-iotests/031 +++ b/tests/qemu-iotests/031 @@ -42,8 +42,9 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 _supported_fmt qcow2 _supported_proto file fuse # We want to test compat=0.10, which does not support external data -# files or refcount widths other than 16 -_unsupported_imgopts data_file 'refcount_bits=\([^1]\|.\([^6]\|$\)\)' +# files or refcount widths other than 16 or compression type +_unsupported_imgopts data_file compression_type \ + 'refcount_bits=\([^1]\|.\([^6]\|$\)\)' CLUSTER_SIZE=65536 diff --git a/tests/qemu-iotests/051 b/tests/qemu-iotests/051 index e9042a6214..f1a506518b 100755 --- a/tests/qemu-iotests/051 +++ b/tests/qemu-iotests/051 @@ -41,8 +41,9 @@ _supported_fmt qcow2 _supported_proto file # A compat=0.10 image is created in this test which does not support anything # other than refcount_bits=16; -# it also will not support an external data file -_unsupported_imgopts 'refcount_bits=\([^1]\|.\([^6]\|$\)\)' data_file +# it also will not support an external data file and compression type +_unsupported_imgopts 'refcount_bits=\([^1]\|.\([^6]\|$\)\)' data_file \ + compression_type _require_drivers nbd if [ "$QEMU_DEFAULT_MACHINE" = "pc" ]; then diff --git a/tests/qemu-iotests/061 b/tests/qemu-iotests/061 index 70edf1a163..513fbec14c 100755 --- a/tests/qemu-iotests/061 +++ b/tests/qemu-iotests/061 @@ -48,7 +48,11 @@ _supported_os Linux # not work with it; # we have explicit tests for various cluster sizes, the remaining tests # require the default 64k cluster -_unsupported_imgopts 'refcount_bits=\([^1]\|.\([^6]\|$\)\)' data_file cluster_size +# we don't have explicit tests for zstd qcow2 compression type, as zstd may be +# not compiled in. And we can't create compat images with comression type +# extension +_unsupported_imgopts 'refcount_bits=\([^1]\|.\([^6]\|$\)\)' data_file \ + cluster_size compression_type echo echo "=== Testing version downgrade with zero expansion ===" diff --git a/tests/qemu-iotests/112 b/tests/qemu-iotests/112 index 07ac74fb2c..5333212993 100755 --- a/tests/qemu-iotests/112 +++ b/tests/qemu-iotests/112 @@ -43,7 +43,8 @@ _supported_proto file fuse # This test will set refcount_bits on its own which would conflict with the # manual setting; compat will be overridden as well; # and external data files do not work well with our refcount testing -_unsupported_imgopts refcount_bits 'compat=0.10' data_file +# also, compression type is not supported with compat=0.10 used in test +_unsupported_imgopts refcount_bits 'compat=0.10' data_file compression_type print_refcount_bits() { diff --git a/tests/qemu-iotests/290 b/tests/qemu-iotests/290 index ed80da2685..776b59de1b 100755 --- a/tests/qemu-iotests/290 +++ b/tests/qemu-iotests/290 @@ -41,7 +41,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 _supported_fmt qcow2 _supported_proto file fuse _supported_os Linux -_unsupported_imgopts 'compat=0.10' refcount_bits data_file +_unsupported_imgopts 'compat=0.10' refcount_bits data_file compression_type echo echo "### Test 'qemu-io -c discard' on a QCOW2 image without a backing file" From 492a119610129f65217580790971fa038e5492d3 Mon Sep 17 00:00:00 2001 From: Hanna Reitz Date: Thu, 25 Nov 2021 14:53:16 +0100 Subject: [PATCH 208/460] block-backend: Retain permissions after migration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After migration, the permissions the guest device wants to impose on its BlockBackend are stored in blk->perm and blk->shared_perm. In blk_root_activate(), we take our permissions, but keep all shared permissions open by calling `blk_set_perm(blk->perm, BLK_PERM_ALL)`. Only afterwards (immediately or later, depending on the runstate) do we restrict the shared permissions by calling `blk_set_perm(blk->perm, blk->shared_perm)`. Unfortunately, our first call with shared_perm=BLK_PERM_ALL has overwritten blk->shared_perm to be BLK_PERM_ALL, so this is a no-op and the set of shared permissions is not restricted. Fix this bug by saving the set of shared permissions before invoking blk_set_perm() with BLK_PERM_ALL and restoring it afterwards. Fixes: 5f7772c4d0cf32f4e779fcd5a69ae4dae24aeebf ("block-backend: Defer shared_perm tightening migration completion") Reported-by: Peng Liang Signed-off-by: Hanna Reitz Message-Id: <20211125135317.186576-2-hreitz@redhat.com> Reviewed-by: Philippe Mathieu-Daudé Tested-by: Peng Liang --- block/block-backend.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/block/block-backend.c b/block/block-backend.c index 23e727199b..4ff6b4d785 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -190,6 +190,7 @@ static void blk_root_activate(BdrvChild *child, Error **errp) { BlockBackend *blk = child->opaque; Error *local_err = NULL; + uint64_t saved_shared_perm; if (!blk->disable_perm) { return; @@ -197,12 +198,22 @@ static void blk_root_activate(BdrvChild *child, Error **errp) blk->disable_perm = false; + /* + * blk->shared_perm contains the permissions we want to share once + * migration is really completely done. For now, we need to share + * all; but we also need to retain blk->shared_perm, which is + * overwritten by a successful blk_set_perm() call. Save it and + * restore it below. + */ + saved_shared_perm = blk->shared_perm; + blk_set_perm(blk, blk->perm, BLK_PERM_ALL, &local_err); if (local_err) { error_propagate(errp, local_err); blk->disable_perm = true; return; } + blk->shared_perm = saved_shared_perm; if (runstate_check(RUN_STATE_INMIGRATE)) { /* Activation can happen when migration process is still active, for From 95fc339c1b189b03ad051359ef9c7868833d3fd5 Mon Sep 17 00:00:00 2001 From: Hanna Reitz Date: Thu, 25 Nov 2021 14:53:17 +0100 Subject: [PATCH 209/460] iotests/migration-permissions: New test This test checks that a raw image in use by a virtio-blk device does not share the WRITE permission both before and after migration. Signed-off-by: Hanna Reitz --- .../qemu-iotests/tests/migration-permissions | 101 ++++++++++++++++++ .../tests/migration-permissions.out | 5 + 2 files changed, 106 insertions(+) create mode 100755 tests/qemu-iotests/tests/migration-permissions create mode 100644 tests/qemu-iotests/tests/migration-permissions.out diff --git a/tests/qemu-iotests/tests/migration-permissions b/tests/qemu-iotests/tests/migration-permissions new file mode 100755 index 0000000000..6be02581c7 --- /dev/null +++ b/tests/qemu-iotests/tests/migration-permissions @@ -0,0 +1,101 @@ +#!/usr/bin/env python3 +# group: migration +# +# Copyright (C) 2021 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 . +# + +import os +import iotests +from iotests import imgfmt, qemu_img_create, qemu_io + + +test_img = os.path.join(iotests.test_dir, 'test.img') +mig_sock = os.path.join(iotests.sock_dir, 'mig.sock') + + +class TestMigrationPermissions(iotests.QMPTestCase): + def setUp(self): + qemu_img_create('-f', imgfmt, test_img, '1M') + + # Set up two VMs (source and destination) accessing the same raw + # image file with a virtio-blk device; prepare the destination for + # migration with .add_incoming() and enable migration events + vms = [None, None] + for i in range(2): + vms[i] = iotests.VM(path_suffix=f'{i}') + vms[i].add_blockdev(f'file,node-name=prot,filename={test_img}') + vms[i].add_blockdev(f'{imgfmt},node-name=fmt,file=prot') + vms[i].add_device('virtio-blk,drive=fmt') + + if i == 1: + vms[i].add_incoming(f'unix:{mig_sock}') + + vms[i].launch() + + result = vms[i].qmp('migrate-set-capabilities', + capabilities=[ + {'capability': 'events', 'state': True} + ]) + self.assert_qmp(result, 'return', {}) + + self.vm_s = vms[0] + self.vm_d = vms[1] + + def tearDown(self): + self.vm_s.shutdown() + self.vm_d.shutdown() + try: + os.remove(mig_sock) + except FileNotFoundError: + pass + os.remove(test_img) + + # Migrate an image in use by a virtio-blk device to another VM and + # verify that the WRITE permission is unshared both before and after + # migration + def test_post_migration_permissions(self): + # Try to access the image R/W, which should fail because virtio-blk + # has not been configured with share-rw=on + log = qemu_io('-f', imgfmt, '-c', 'quit', test_img) + if not log.strip(): + print('ERROR (pre-migration): qemu-io should not be able to ' + 'access this image, but it reported no error') + else: + # This is the expected output + assert 'Is another process using the image' in log + + # Now migrate the VM + self.vm_s.qmp('migrate', uri=f'unix:{mig_sock}') + assert self.vm_s.wait_migration(None) + assert self.vm_d.wait_migration(None) + + # Try the same qemu-io access again, verifying that the WRITE + # permission remains unshared + log = qemu_io('-f', imgfmt, '-c', 'quit', test_img) + if not log.strip(): + print('ERROR (post-migration): qemu-io should not be able to ' + 'access this image, but it reported no error') + else: + # This is the expected output + assert 'Is another process using the image' in log + + +if __name__ == '__main__': + # Only works with raw images because we are testing the + # BlockBackend permissions; image format drivers may additionally + # unshare permissions and thus tamper with the result + iotests.main(supported_fmts=['raw'], + supported_protocols=['file']) diff --git a/tests/qemu-iotests/tests/migration-permissions.out b/tests/qemu-iotests/tests/migration-permissions.out new file mode 100644 index 0000000000..ae1213e6f8 --- /dev/null +++ b/tests/qemu-iotests/tests/migration-permissions.out @@ -0,0 +1,5 @@ +. +---------------------------------------------------------------------- +Ran 1 tests + +OK From 751486c18555169ca4baf59440275d5831140822 Mon Sep 17 00:00:00 2001 From: Emanuele Giuseppe Esposito Date: Mon, 31 Jan 2022 07:56:15 -0500 Subject: [PATCH 210/460] block.h: remove outdated comment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The comment "disk I/O throttling" doesn't make any sense at all any more. It was added in commit 0563e191516 to describe bdrv_io_limits_enable()/disable(), which were removed in commit 97148076, so the comment is just a forgotten leftover. Suggested-by: Kevin Wolf Signed-off-by: Emanuele Giuseppe Esposito Message-Id: <20220131125615.74612-1-eesposit@redhat.com> Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Hanna Reitz --- include/block/block.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/block/block.h b/include/block/block.h index 9d4050220b..e1713ee306 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -344,7 +344,6 @@ typedef unsigned int BdrvChildRole; char *bdrv_perm_names(uint64_t perm); uint64_t bdrv_qapi_perm_to_blk_perm(BlockPermission qapi_perm); -/* disk I/O throttling */ void bdrv_init(void); void bdrv_init_with_whitelist(void); bool bdrv_uses_whitelist(void); From e66e665f15736f5ee1fbd8087926cb0f1e52f61a Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Tue, 25 Jan 2022 16:15:14 +0100 Subject: [PATCH 211/460] qemu-storage-daemon: Fix typo in vhost-user-blk help The syntax of the fd passing case misses the "addr.type=" key. Add it. Signed-off-by: Kevin Wolf Message-Id: <20220125151514.49035-1-kwolf@redhat.com> Reviewed-by: Hanna Reitz Signed-off-by: Kevin Wolf --- storage-daemon/qemu-storage-daemon.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage-daemon/qemu-storage-daemon.c b/storage-daemon/qemu-storage-daemon.c index 9d76d1114d..ec9aa79b55 100644 --- a/storage-daemon/qemu-storage-daemon.c +++ b/storage-daemon/qemu-storage-daemon.c @@ -111,7 +111,7 @@ static void help(void) " export the specified block node as a\n" " vhost-user-blk device over UNIX domain socket\n" " --export [type=]vhost-user-blk,id=,node-name=,\n" -" fd,addr.str=[,writable=on|off]\n" +" addr.type=fd,addr.str=[,writable=on|off]\n" " [,logical-block-size=][,num-queues=]\n" " export the specified block node as a\n" " vhost-user-blk device over file descriptor\n" From c0829cb1fd5e0b35abfcf9fc3f04502c1ed5d7b6 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Mon, 24 Jan 2022 18:37:41 +0100 Subject: [PATCH 212/460] block: bdrv_set_backing_hd(): use drained section Graph modifications should be done in drained section. stream_prepare() handler of block stream job call bdrv_set_backing_hd() without using drained section and it's theoretically possible that some IO request will interleave with graph modification and will use outdated pointers to removed block nodes. Some other callers use bdrv_set_backing_hd() not caring about drained sections too. So it seems good to make a drained section exactly in bdrv_set_backing_hd(). Signed-off-by: Vladimir Sementsov-Ogievskiy Message-Id: <20220124173741.2984056-1-vsementsov@virtuozzo.com> Signed-off-by: Kevin Wolf --- block.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/block.c b/block.c index 7b3ce415d8..b54d59d1fa 100644 --- a/block.c +++ b/block.c @@ -3341,6 +3341,8 @@ int bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd, int ret; Transaction *tran = tran_new(); + bdrv_drained_begin(bs); + ret = bdrv_set_backing_noperm(bs, backing_hd, tran, errp); if (ret < 0) { goto out; @@ -3350,6 +3352,8 @@ int bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd, out: tran_finalize(tran, ret); + bdrv_drained_end(bs); + return ret; } From 520d8b40e898158bc9a2b416d1cbdb44d2260bc7 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Tue, 25 Jan 2022 16:14:35 +0100 Subject: [PATCH 213/460] block/export: Fix vhost-user-blk shutdown with requests in flight The vhost-user-blk export runs requests asynchronously in their own coroutine. When the vhost connection goes away and we want to stop the vhost-user server, we need to wait for these coroutines to stop before we can unmap the shared memory. Otherwise, they would still access the unmapped memory and crash. This introduces a refcount to VuServer which is increased when spawning a new request coroutine and decreased before the coroutine exits. The memory is only unmapped when the refcount reaches zero. Signed-off-by: Kevin Wolf Message-Id: <20220125151435.48792-1-kwolf@redhat.com> Signed-off-by: Kevin Wolf --- block/export/vhost-user-blk-server.c | 5 +++++ include/qemu/vhost-user-server.h | 5 +++++ util/vhost-user-server.c | 22 ++++++++++++++++++++++ 3 files changed, 32 insertions(+) diff --git a/block/export/vhost-user-blk-server.c b/block/export/vhost-user-blk-server.c index 1862563336..a129204c44 100644 --- a/block/export/vhost-user-blk-server.c +++ b/block/export/vhost-user-blk-server.c @@ -172,6 +172,7 @@ vu_blk_discard_write_zeroes(VuBlkExport *vexp, struct iovec *iov, return VIRTIO_BLK_S_IOERR; } +/* Called with server refcount increased, must decrease before returning */ static void coroutine_fn vu_blk_virtio_process_req(void *opaque) { VuBlkReq *req = opaque; @@ -286,10 +287,12 @@ static void coroutine_fn vu_blk_virtio_process_req(void *opaque) } vu_blk_req_complete(req); + vhost_user_server_unref(server); return; err: free(req); + vhost_user_server_unref(server); } static void vu_blk_process_vq(VuDev *vu_dev, int idx) @@ -310,6 +313,8 @@ static void vu_blk_process_vq(VuDev *vu_dev, int idx) Coroutine *co = qemu_coroutine_create(vu_blk_virtio_process_req, req); + + vhost_user_server_ref(server); qemu_coroutine_enter(co); } } diff --git a/include/qemu/vhost-user-server.h b/include/qemu/vhost-user-server.h index 121ea1dedf..cd43193b80 100644 --- a/include/qemu/vhost-user-server.h +++ b/include/qemu/vhost-user-server.h @@ -42,6 +42,8 @@ typedef struct { const VuDevIface *vu_iface; /* Protected by ctx lock */ + unsigned int refcount; + bool wait_idle; VuDev vu_dev; QIOChannel *ioc; /* The I/O channel with the client */ QIOChannelSocket *sioc; /* The underlying data channel with the client */ @@ -59,6 +61,9 @@ bool vhost_user_server_start(VuServer *server, void vhost_user_server_stop(VuServer *server); +void vhost_user_server_ref(VuServer *server); +void vhost_user_server_unref(VuServer *server); + void vhost_user_server_attach_aio_context(VuServer *server, AioContext *ctx); void vhost_user_server_detach_aio_context(VuServer *server); diff --git a/util/vhost-user-server.c b/util/vhost-user-server.c index f68287e811..f66fbba710 100644 --- a/util/vhost-user-server.c +++ b/util/vhost-user-server.c @@ -74,6 +74,20 @@ static void panic_cb(VuDev *vu_dev, const char *buf) error_report("vu_panic: %s", buf); } +void vhost_user_server_ref(VuServer *server) +{ + assert(!server->wait_idle); + server->refcount++; +} + +void vhost_user_server_unref(VuServer *server) +{ + server->refcount--; + if (server->wait_idle && !server->refcount) { + aio_co_wake(server->co_trip); + } +} + static bool coroutine_fn vu_message_read(VuDev *vu_dev, int conn_fd, VhostUserMsg *vmsg) { @@ -177,6 +191,14 @@ static coroutine_fn void vu_client_trip(void *opaque) /* Keep running */ } + if (server->refcount) { + /* Wait for requests to complete before we can unmap the memory */ + server->wait_idle = true; + qemu_coroutine_yield(); + server->wait_idle = false; + } + assert(server->refcount == 0); + vu_deinit(vu_dev); /* vu_deinit() should have called remove_watch() */ From ac50419460cc45a66214e6d1c3e9d0d670522f8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 1 Feb 2022 12:26:54 +0100 Subject: [PATCH 214/460] block/export/fuse: Rearrange if-else-if ladder in fuse_fallocate() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In order to safely maintain a mixture of #ifdef'ry with if-else-if ladder, rearrange the last statement (!mode) first. Since it is mutually exclusive with the other conditions, checking it first doesn't make any logical difference, but allows to add #ifdef'ry around in a more cleanly way. Suggested-by: Kevin Wolf Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20220201112655.344373-2-f4bug@amsat.org> Signed-off-by: Kevin Wolf --- block/export/fuse.c | 41 +++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/block/export/fuse.c b/block/export/fuse.c index 6710d8aed8..d25e478c0a 100644 --- a/block/export/fuse.c +++ b/block/export/fuse.c @@ -629,7 +629,26 @@ static void fuse_fallocate(fuse_req_t req, fuse_ino_t inode, int mode, length = MIN(length, blk_len - offset); } - if (mode & FALLOC_FL_PUNCH_HOLE) { + if (!mode) { + /* We can only fallocate at the EOF with a truncate */ + if (offset < blk_len) { + fuse_reply_err(req, EOPNOTSUPP); + return; + } + + if (offset > blk_len) { + /* No preallocation needed here */ + ret = fuse_do_truncate(exp, offset, true, PREALLOC_MODE_OFF); + if (ret < 0) { + fuse_reply_err(req, -ret); + return; + } + } + + ret = fuse_do_truncate(exp, offset + length, true, + PREALLOC_MODE_FALLOC); + } + else if (mode & FALLOC_FL_PUNCH_HOLE) { if (!(mode & FALLOC_FL_KEEP_SIZE)) { fuse_reply_err(req, EINVAL); return; @@ -665,25 +684,7 @@ static void fuse_fallocate(fuse_req_t req, fuse_ino_t inode, int mode, } while (ret == 0 && length > 0); } #endif /* CONFIG_FALLOCATE_ZERO_RANGE */ - else if (!mode) { - /* We can only fallocate at the EOF with a truncate */ - if (offset < blk_len) { - fuse_reply_err(req, EOPNOTSUPP); - return; - } - - if (offset > blk_len) { - /* No preallocation needed here */ - ret = fuse_do_truncate(exp, offset, true, PREALLOC_MODE_OFF); - if (ret < 0) { - fuse_reply_err(req, -ret); - return; - } - } - - ret = fuse_do_truncate(exp, offset + length, true, - PREALLOC_MODE_FALLOC); - } else { + else { ret = -EOPNOTSUPP; } From 3c9c70347b8e636c08035f39288f8cdd2e68bbda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 1 Feb 2022 12:26:55 +0100 Subject: [PATCH 215/460] block/export/fuse: Fix build failure on FreeBSD MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When building on FreeBSD we get: [816/6851] Compiling C object libblockdev.fa.p/block_export_fuse.c.o ../block/export/fuse.c:628:16: error: use of undeclared identifier 'FALLOC_FL_KEEP_SIZE' if (mode & FALLOC_FL_KEEP_SIZE) { ^ ../block/export/fuse.c:651:16: error: use of undeclared identifier 'FALLOC_FL_PUNCH_HOLE' if (mode & FALLOC_FL_PUNCH_HOLE) { ^ ../block/export/fuse.c:652:22: error: use of undeclared identifier 'FALLOC_FL_KEEP_SIZE' if (!(mode & FALLOC_FL_KEEP_SIZE)) { ^ 3 errors generated. FAILED: libblockdev.fa.p/block_export_fuse.c.o Meson indeed reported FALLOC_FL_PUNCH_HOLE is not available: C compiler for the host machine: cc (clang 10.0.1 "FreeBSD clang version 10.0.1") Checking for function "fallocate" : NO Checking for function "posix_fallocate" : YES Header has symbol "FALLOC_FL_PUNCH_HOLE" : NO Header has symbol "FALLOC_FL_ZERO_RANGE" : NO ... Similarly to commit 304332039 ("block/export/fuse.c: fix musl build"), guard the code requiring FALLOC_FL_KEEP_SIZE / FALLOC_FL_PUNCH_HOLE definitions under CONFIG_FALLOCATE_PUNCH_HOLE #ifdef'ry. Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20220201112655.344373-3-f4bug@amsat.org> Signed-off-by: Kevin Wolf --- block/export/fuse.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/block/export/fuse.c b/block/export/fuse.c index d25e478c0a..fdda8e3c81 100644 --- a/block/export/fuse.c +++ b/block/export/fuse.c @@ -625,9 +625,11 @@ static void fuse_fallocate(fuse_req_t req, fuse_ino_t inode, int mode, return; } +#ifdef CONFIG_FALLOCATE_PUNCH_HOLE if (mode & FALLOC_FL_KEEP_SIZE) { length = MIN(length, blk_len - offset); } +#endif /* CONFIG_FALLOCATE_PUNCH_HOLE */ if (!mode) { /* We can only fallocate at the EOF with a truncate */ @@ -648,6 +650,7 @@ static void fuse_fallocate(fuse_req_t req, fuse_ino_t inode, int mode, ret = fuse_do_truncate(exp, offset + length, true, PREALLOC_MODE_FALLOC); } +#ifdef CONFIG_FALLOCATE_PUNCH_HOLE else if (mode & FALLOC_FL_PUNCH_HOLE) { if (!(mode & FALLOC_FL_KEEP_SIZE)) { fuse_reply_err(req, EINVAL); @@ -662,6 +665,7 @@ static void fuse_fallocate(fuse_req_t req, fuse_ino_t inode, int mode, length -= size; } while (ret == 0 && length > 0); } +#endif /* CONFIG_FALLOCATE_PUNCH_HOLE */ #ifdef CONFIG_FALLOCATE_ZERO_RANGE else if (mode & FALLOC_FL_ZERO_RANGE) { if (!(mode & FALLOC_FL_KEEP_SIZE) && offset + length > blk_len) { From ef6ec0d7793bec9e529fd967c25c8300ef6b3cd2 Mon Sep 17 00:00:00 2001 From: Emanuele Giuseppe Esposito Date: Mon, 31 Jan 2022 07:56:15 -0500 Subject: [PATCH 216/460] block.h: remove outdated comment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The comment "disk I/O throttling" doesn't make any sense at all any more. It was added in commit 0563e191516 to describe bdrv_io_limits_enable()/disable(), which were removed in commit 97148076, so the comment is just a forgotten leftover. Suggested-by: Kevin Wolf Signed-off-by: Emanuele Giuseppe Esposito Message-Id: <20220131125615.74612-1-eesposit@redhat.com> Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Kevin Wolf --- include/block/block.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/block/block.h b/include/block/block.h index 9d4050220b..e1713ee306 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -344,7 +344,6 @@ typedef unsigned int BdrvChildRole; char *bdrv_perm_names(uint64_t perm); uint64_t bdrv_qapi_perm_to_blk_perm(BlockPermission qapi_perm); -/* disk I/O throttling */ void bdrv_init(void); void bdrv_init_with_whitelist(void); bool bdrv_uses_whitelist(void); From cb90ec3a3646a3bdb0b2a157db226db25d470442 Mon Sep 17 00:00:00 2001 From: Hanna Reitz Date: Mon, 31 Jan 2022 11:31:24 +0100 Subject: [PATCH 217/460] qsd: Document fuse's allow-other option We did not add documentation to the storage daemon's man page for fuse's allow-other option when it was introduced, so do that now. Fixes: 8fc54f9428b9763f800 ("export/fuse: Add allow-other option") Signed-off-by: Hanna Reitz Message-Id: <20220131103124.20325-1-hreitz@redhat.com> Reviewed-by: Eric Blake Signed-off-by: Kevin Wolf --- docs/tools/qemu-storage-daemon.rst | 9 +++++++-- storage-daemon/qemu-storage-daemon.c | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/docs/tools/qemu-storage-daemon.rst b/docs/tools/qemu-storage-daemon.rst index 9b0eaba6e5..878e6a5c5c 100644 --- a/docs/tools/qemu-storage-daemon.rst +++ b/docs/tools/qemu-storage-daemon.rst @@ -76,7 +76,7 @@ Standard options: .. option:: --export [type=]nbd,id=,node-name=[,name=][,writable=on|off][,bitmap=] --export [type=]vhost-user-blk,id=,node-name=,addr.type=unix,addr.path=[,writable=on|off][,logical-block-size=][,num-queues=] --export [type=]vhost-user-blk,id=,node-name=,addr.type=fd,addr.str=[,writable=on|off][,logical-block-size=][,num-queues=] - --export [type=]fuse,id=,node-name=,mountpoint=[,growable=on|off][,writable=on|off] + --export [type=]fuse,id=,node-name=,mountpoint=[,growable=on|off][,writable=on|off][,allow-other=on|off|auto] is a block export definition. ``node-name`` is the block node that should be exported. ``writable`` determines whether or not the export allows write @@ -103,7 +103,12 @@ Standard options: mounted). Consequently, applications that have opened the given file before the export became active will continue to see its original content. If ``growable`` is set, writes after the end of the exported file will grow the - block node to fit. + block node to fit. The ``allow-other`` option controls whether users other + than the user running the process will be allowed to access the export. Note + that enabling this option as a non-root user requires enabling the + user_allow_other option in the global fuse.conf configuration file. Setting + ``allow-other`` to auto (the default) will try enabling this option, and on + error fall back to disabling it. .. option:: --monitor MONITORDEF diff --git a/storage-daemon/qemu-storage-daemon.c b/storage-daemon/qemu-storage-daemon.c index ec9aa79b55..504d33aa91 100644 --- a/storage-daemon/qemu-storage-daemon.c +++ b/storage-daemon/qemu-storage-daemon.c @@ -100,7 +100,7 @@ static void help(void) "\n" #ifdef CONFIG_FUSE " --export [type=]fuse,id=,node-name=,mountpoint=\n" -" [,growable=on|off][,writable=on|off]\n" +" [,growable=on|off][,writable=on|off][,allow-other=on|off|auto]\n" " export the specified block node over FUSE\n" "\n" #endif /* CONFIG_FUSE */ From 111fbd74f67575c158d9be5363825aab8be50a0a Mon Sep 17 00:00:00 2001 From: Hanna Reitz Date: Mon, 31 Jan 2022 14:59:08 +0100 Subject: [PATCH 218/460] qemu-img: Unify [-b [-F]] documentation qemu-img convert documents the backing file and backing format options as follows: [-B backing_file [-F backing_fmt]] whereas qemu-img create has this: [-b backing_file] [-F backing_fmt] That is, for convert, we document that -F cannot be given without -B, while for create, way say that they are independent. Indeed, it is technically possible to give -F without -b, because it is left to the block driver to decide whether this is an error or not, so sometimes it is: $ qemu-img create -f qed -F qed test.qed 64M Formatting 'test.qed', fmt=qed size=67108864 backing_fmt=qed [...] And sometimes it is not: $ qemu-img create -f qcow2 -F qcow2 test.qcow2 64M Formatting 'test.qcow2', fmt=qcow2 cluster_size=65536 [...] qemu-img: test.qcow2: Backing format cannot be used without backing file Generally, it does not make much sense, though, and users should only give -F with -b, so document it that way, as we have already done for qemu-img convert (commit 1899bf47375ad40555dcdff12ba49b4b8b82df38). Reported-by: Tingting Mao Signed-off-by: Hanna Reitz Message-Id: <20220131135908.32393-1-hreitz@redhat.com> Signed-off-by: Kevin Wolf --- docs/tools/qemu-img.rst | 2 +- qemu-img-cmds.hx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/tools/qemu-img.rst b/docs/tools/qemu-img.rst index d663dd92bd..8885ea11cf 100644 --- a/docs/tools/qemu-img.rst +++ b/docs/tools/qemu-img.rst @@ -463,7 +463,7 @@ Command description: ``--skip-broken-bitmaps`` is also specified to copy only the consistent bitmaps. -.. option:: create [--object OBJECTDEF] [-q] [-f FMT] [-b BACKING_FILE] [-F BACKING_FMT] [-u] [-o OPTIONS] FILENAME [SIZE] +.. option:: create [--object OBJECTDEF] [-q] [-f FMT] [-b BACKING_FILE [-F BACKING_FMT]] [-u] [-o OPTIONS] FILENAME [SIZE] Create the new disk image *FILENAME* of size *SIZE* and format *FMT*. Depending on the file format, you can add one or more *OPTIONS* diff --git a/qemu-img-cmds.hx b/qemu-img-cmds.hx index 72bcdcfbfa..1b1dab5b17 100644 --- a/qemu-img-cmds.hx +++ b/qemu-img-cmds.hx @@ -52,9 +52,9 @@ SRST ERST DEF("create", img_create, - "create [--object objectdef] [-q] [-f fmt] [-b backing_file] [-F backing_fmt] [-u] [-o options] filename [size]") + "create [--object objectdef] [-q] [-f fmt] [-b backing_file [-F backing_fmt]] [-u] [-o options] filename [size]") SRST -.. option:: create [--object OBJECTDEF] [-q] [-f FMT] [-b BACKING_FILE] [-F BACKING_FMT] [-u] [-o OPTIONS] FILENAME [SIZE] +.. option:: create [--object OBJECTDEF] [-q] [-f FMT] [-b BACKING_FILE [-F BACKING_FMT]] [-u] [-o OPTIONS] FILENAME [SIZE] ERST DEF("dd", img_dd, From 9e302f64bb407a9bb097b626da97228c2654cfee Mon Sep 17 00:00:00 2001 From: Peter Lieven Date: Thu, 13 Jan 2022 15:44:25 +0100 Subject: [PATCH 219/460] block/rbd: fix handling of holes in .bdrv_co_block_status the assumption that we can't hit a hole if we do not diff against a snapshot was wrong. We can see a hole in an image if we diff against base if there exists an older snapshot of the image and we have discarded blocks in the image where the snapshot has data. Fix this by simply handling a hole like an unallocated area. There are no callbacks for unallocated areas so just bail out if we hit a hole. Fixes: 0347a8fd4c3faaedf119be04c197804be40a384b Suggested-by: Ilya Dryomov Cc: qemu-stable@nongnu.org Signed-off-by: Peter Lieven Message-Id: <20220113144426.4036493-2-pl@kamp.de> Reviewed-by: Ilya Dryomov Reviewed-by: Stefano Garzarella Signed-off-by: Kevin Wolf --- block/rbd.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/block/rbd.c b/block/rbd.c index def96292e0..20bb896c4a 100644 --- a/block/rbd.c +++ b/block/rbd.c @@ -1279,11 +1279,11 @@ static int qemu_rbd_diff_iterate_cb(uint64_t offs, size_t len, RBDDiffIterateReq *req = opaque; assert(req->offs + req->bytes <= offs); - /* - * we do not diff against a snapshot so we should never receive a callback - * for a hole. - */ - assert(exists); + + /* treat a hole like an unallocated area and bail out */ + if (!exists) { + return 0; + } if (!req->exists && offs > req->offs) { /* From fc176116cdea816ceb8dd969080b2b95f58edbc0 Mon Sep 17 00:00:00 2001 From: Peter Lieven Date: Thu, 13 Jan 2022 15:44:26 +0100 Subject: [PATCH 220/460] block/rbd: workaround for ceph issue #53784 librbd had a bug until early 2022 that affected all versions of ceph that supported fast-diff. This bug results in reporting of incorrect offsets if the offset parameter to rbd_diff_iterate2 is not object aligned. This patch works around this bug for pre Quincy versions of librbd. Fixes: 0347a8fd4c3faaedf119be04c197804be40a384b Cc: qemu-stable@nongnu.org Signed-off-by: Peter Lieven Message-Id: <20220113144426.4036493-3-pl@kamp.de> Reviewed-by: Ilya Dryomov Reviewed-by: Stefano Garzarella Tested-by: Stefano Garzarella Signed-off-by: Kevin Wolf --- block/rbd.c | 42 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/block/rbd.c b/block/rbd.c index 20bb896c4a..8f183eba2a 100644 --- a/block/rbd.c +++ b/block/rbd.c @@ -1320,6 +1320,7 @@ static int coroutine_fn qemu_rbd_co_block_status(BlockDriverState *bs, int status, r; RBDDiffIterateReq req = { .offs = offset }; uint64_t features, flags; + uint64_t head = 0; assert(offset + bytes <= s->image_size); @@ -1347,7 +1348,43 @@ static int coroutine_fn qemu_rbd_co_block_status(BlockDriverState *bs, return status; } - r = rbd_diff_iterate2(s->image, NULL, offset, bytes, true, true, +#if LIBRBD_VERSION_CODE < LIBRBD_VERSION(1, 17, 0) + /* + * librbd had a bug until early 2022 that affected all versions of ceph that + * supported fast-diff. This bug results in reporting of incorrect offsets + * if the offset parameter to rbd_diff_iterate2 is not object aligned. + * Work around this bug by rounding down the offset to object boundaries. + * This is OK because we call rbd_diff_iterate2 with whole_object = true. + * However, this workaround only works for non cloned images with default + * striping. + * + * See: https://tracker.ceph.com/issues/53784 + */ + + /* check if RBD image has non-default striping enabled */ + if (features & RBD_FEATURE_STRIPINGV2) { + return status; + } + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + /* + * check if RBD image is a clone (= has a parent). + * + * rbd_get_parent_info is deprecated from Nautilus onwards, but the + * replacement rbd_get_parent is not present in Luminous and Mimic. + */ + if (rbd_get_parent_info(s->image, NULL, 0, NULL, 0, NULL, 0) != -ENOENT) { + return status; + } +#pragma GCC diagnostic pop + + head = req.offs & (s->object_size - 1); + req.offs -= head; + bytes += head; +#endif + + r = rbd_diff_iterate2(s->image, NULL, req.offs, bytes, true, true, qemu_rbd_diff_iterate_cb, &req); if (r < 0 && r != QEMU_RBD_EXIT_DIFF_ITERATE2) { return status; @@ -1366,7 +1403,8 @@ static int coroutine_fn qemu_rbd_co_block_status(BlockDriverState *bs, status = BDRV_BLOCK_ZERO | BDRV_BLOCK_OFFSET_VALID; } - *pnum = req.bytes; + assert(req.bytes > head); + *pnum = req.bytes - head; return status; } From eb9d35f686ed1279d57463d9e6f289988f594c19 Mon Sep 17 00:00:00 2001 From: Warner Losh Date: Tue, 1 Feb 2022 13:30:30 -0700 Subject: [PATCH 221/460] bsd-user/signal.c: Only copy the _capsicum for FreeBSD_version > 1400026 The capsicum signal stuff is new with FreeBSD 14, rev 1400026, so only define QEMU_SI_CAPSICUM there. Only copy _capsicum when QEMU_SI_CAPSICUM is defined. Default to no info being passed for signals we make no guess about. Signed-off-by: Warner Losh Reviewed-by: Richard Henderson --- bsd-user/signal-common.h | 5 +++++ bsd-user/signal.c | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/bsd-user/signal-common.h b/bsd-user/signal-common.h index 7ff8e8f2e4..6f90345bb2 100644 --- a/bsd-user/signal-common.h +++ b/bsd-user/signal-common.h @@ -59,12 +59,17 @@ void target_to_host_sigset(sigset_t *d, const target_sigset_t *s); * For FreeBSD, we have si_pid, si_uid, si_status, and si_addr always. Linux and * {Open,Net}BSD have a different approach (where their reason field is larger, * but whose siginfo has fewer fields always). + * + * QEMU_SI_CAPSICUM is currently only FreeBSD 14 current only, so only define + * it where _capsicum is available. */ #define QEMU_SI_NOINFO 0 /* nothing other than si_signo valid */ #define QEMU_SI_FAULT 1 /* _fault is valid in _reason */ #define QEMU_SI_TIMER 2 /* _timer is valid in _reason */ #define QEMU_SI_MESGQ 3 /* _mesgq is valid in _reason */ #define QEMU_SI_POLL 4 /* _poll is valid in _reason */ +#if defined(__FreeBSD_version) && __FreeBSD_version >= 1400026 #define QEMU_SI_CAPSICUM 5 /* _capsicum is valid in _reason */ +#endif #endif diff --git a/bsd-user/signal.c b/bsd-user/signal.c index ad22ba9d90..0bc6d2edbd 100644 --- a/bsd-user/signal.c +++ b/bsd-user/signal.c @@ -222,6 +222,7 @@ static inline void host_to_target_siginfo_noswap(target_siginfo_t *tinfo, * We have to go based on the signal number now to figure out * what's valid. */ + si_type = QEMU_SI_NOINFO; if (has_trapno(sig)) { tinfo->_reason._fault._trapno = info->_reason._fault._trapno; si_type = QEMU_SI_FAULT; @@ -241,11 +242,13 @@ static inline void host_to_target_siginfo_noswap(target_siginfo_t *tinfo, * capsicum is somewhere between weak and non-existant, but if we get * one, then we know what to save. */ +#ifdef QEMU_SI_CAPSICUM if (sig == TARGET_SIGTRAP) { tinfo->_reason._capsicum._syscall = info->_reason._capsicum._syscall; si_type = QEMU_SI_CAPSICUM; } +#endif break; } tinfo->si_code = deposit32(si_code, 24, 8, si_type); @@ -295,10 +298,12 @@ static void tswap_siginfo(target_siginfo_t *tinfo, const target_siginfo_t *info) /* Note: Not generated on FreeBSD */ __put_user(info->_reason._poll._band, &tinfo->_reason._poll._band); break; +#ifdef QEMU_SI_CAPSICUM case QEMU_SI_CAPSICUM: __put_user(info->_reason._capsicum._syscall, &tinfo->_reason._capsicum._syscall); break; +#endif default: g_assert_not_reached(); } From 097defeb12eed05e637436db65c117c4b6274f9d Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Wed, 2 Feb 2022 18:45:02 +0100 Subject: [PATCH 222/460] seabios-hppa: Update SeaBIOS-hppa to VERSION 3 New firmware features and fixes: * Allow up to 16 CPUs * Add TOC button support: To trigger a TOC, execute "nmi" in the qemu monitor (Ctrl-A C) * New opt/hostid fw_cfg option to change hostid: -fw_cfg opt/hostid,string=334455 * Add opt/console fw_cfg option to select default console: -fw_cfg opt/console,string=serial -fw_cfg opt/console,string=graphics * Add Linux TER16x32 font to STI firmware: -fw_cfg opt/font,string=2 * Leave IRQs disabled after rendevouz Signed-off-by: Helge Deller --- pc-bios/hppa-firmware.img | Bin 757144 -> 701964 bytes roms/seabios-hppa | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/pc-bios/hppa-firmware.img b/pc-bios/hppa-firmware.img index a34a8c84f5d96419bbe7baf0b2fed1842a450cdd..0ec2d96b8f48a46dc9c0fabd50d14d5a2212a071 100644 GIT binary patch literal 701964 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�!2IIv{wy~btiB;%3Ak4E$1Hn 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

GG7 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 AHvj+t literal 757144 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 LeC;v+Q@sBJ=?t(~ diff --git a/roms/seabios-hppa b/roms/seabios-hppa index b12acac4be..bf3404006f 160000 --- a/roms/seabios-hppa +++ b/roms/seabios-hppa @@ -1 +1 @@ -Subproject commit b12acac4be27b6d5d9fbe48c4be1286dcc245fbb +Subproject commit bf3404006fd2c832857eb57e6f853862f97dacea From 87e126ea149ee3f19d64f054886f573dc6b8ddee Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Tue, 11 Jan 2022 20:44:42 +0100 Subject: [PATCH 223/460] hw/hppa: Allow up to 16 emulated CPUs This brings the hppa_hardware.h file in sync with the copy in the SeaBIOS-hppa sources. In order to support up to 16 CPUs, it's required to move the HPA for MEMORY_HPA out of the address space of the new 16th CPU. The new address of 0xfffff000 worked well for Linux and HP-UX, while other addresses close to the former 0xfffbf000 area are used by the architecture for local and global broadcasts. The PIM_STORAGE_SIZE constant is used in SeaBIOS sources and is relevant for the TOC/NMI feature. Signed-off-by: Helge Deller Reviewed-by: Richard Henderson --- hw/hppa/hppa_hardware.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/hw/hppa/hppa_hardware.h b/hw/hppa/hppa_hardware.h index bc258895c9..5edf577563 100644 --- a/hw/hppa/hppa_hardware.h +++ b/hw/hppa/hppa_hardware.h @@ -25,7 +25,7 @@ #define LASI_GFX_HPA 0xf8000000 #define ARTIST_FB_ADDR 0xf9000000 #define CPU_HPA 0xfffb0000 -#define MEMORY_HPA 0xfffbf000 +#define MEMORY_HPA 0xfffff000 #define PCI_HPA DINO_HPA /* PCI bus */ #define IDE_HPA 0xf9000000 /* Boot disc controller */ @@ -43,9 +43,10 @@ #define PORT_SERIAL1 (DINO_UART_HPA + 0x800) #define PORT_SERIAL2 (LASI_UART_HPA + 0x800) -#define HPPA_MAX_CPUS 8 /* max. number of SMP CPUs */ +#define HPPA_MAX_CPUS 16 /* max. number of SMP CPUs */ #define CPU_CLOCK_MHZ 250 /* emulate a 250 MHz CPU */ #define CPU_HPA_CR_REG 7 /* store CPU HPA in cr7 (SeaBIOS internal) */ +#define PIM_STORAGE_SIZE 600 /* storage size of pdc_pim_toc_struct (64bit) */ #endif From 4a4554c6c561971197dffc80d641dc17ee2314dd Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Wed, 5 Jan 2022 23:09:04 +0100 Subject: [PATCH 224/460] hppa: Add support for an emulated TOC/NMI button. Almost all PA-RISC machines have either a button that is labeled with 'TOC' or a BMC/GSP function to trigger a TOC. TOC is a non-maskable interrupt that is sent to the processor. This can be used for diagnostic purposes like obtaining a stack trace/register dump or to enter KDB/KGDB in Linux. This patch adds support for such an emulated TOC button. It wires up the qemu monitor "nmi" command to trigger a TOC. For that it provides the hppa_nmi function which is assigned to the nmi_monitor_handler function pointer. When called it raises the EXCP_TOC hardware interrupt in the hppa_cpu_do_interrupt() function. The interrupt function then calls the architecturally defined TOC function in SeaBIOS-hppa firmware (at fixed address 0xf0000000). According to the PA-RISC PDC specification, the SeaBIOS firmware then writes the CPU registers into PIM (processor internal memmory) for later analysis. In order to write all registers it needs to know the contents of the CPU "shadow registers" and the IASQ- and IAOQ-back values. The IAOQ/IASQ values are provided by qemu in shadow registers when entering the SeaBIOS TOC function. This patch adds a new aritificial opcode "getshadowregs" (0xfffdead2) which restores the original values of the shadow registers. With this opcode SeaBIOS can store those registers as well into PIM before calling an OS-provided TOC handler. To trigger a TOC, switch to the qemu monitor with Ctrl-A C, and type in the command "nmi". After the TOC started the OS-debugger, exit the qemu monitor with Ctrl-A C. Signed-off-by: Helge Deller Reviewed-by: Richard Henderson --- hw/hppa/machine.c | 35 ++++++++++++++++++++++++++++++++++- target/hppa/cpu.c | 2 +- target/hppa/cpu.h | 5 +++++ target/hppa/helper.h | 1 + target/hppa/insns.decode | 1 + target/hppa/int_helper.c | 19 ++++++++++++++++++- target/hppa/op_helper.c | 7 ++++++- target/hppa/translate.c | 10 ++++++++++ 8 files changed, 76 insertions(+), 4 deletions(-) diff --git a/hw/hppa/machine.c b/hw/hppa/machine.c index 2a46af5bc9..98b30e0395 100644 --- a/hw/hppa/machine.c +++ b/hw/hppa/machine.c @@ -17,6 +17,7 @@ #include "hw/timer/i8254.h" #include "hw/char/serial.h" #include "hw/net/lasi_82596.h" +#include "hw/nmi.h" #include "hppa_sys.h" #include "qemu/units.h" #include "qapi/error.h" @@ -355,6 +356,14 @@ static void hppa_machine_reset(MachineState *ms) cpu[0]->env.gr[19] = FW_CFG_IO_BASE; } +static void hppa_nmi(NMIState *n, int cpu_index, Error **errp) +{ + CPUState *cs; + + CPU_FOREACH(cs) { + cpu_interrupt(cs, CPU_INTERRUPT_NMI); + } +} static void machine_hppa_machine_init(MachineClass *mc) { @@ -371,4 +380,28 @@ static void machine_hppa_machine_init(MachineClass *mc) mc->default_ram_id = "ram"; } -DEFINE_MACHINE("hppa", machine_hppa_machine_init) +static void machine_hppa_machine_init_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + machine_hppa_machine_init(mc); + + NMIClass *nc = NMI_CLASS(oc); + nc->nmi_monitor_handler = hppa_nmi; +} + +static const TypeInfo machine_hppa_machine_init_typeinfo = { + .name = ("hppa" "-machine"), + .parent = "machine", + .class_init = machine_hppa_machine_init_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_NMI }, + { } + }, +}; + +static void machine_hppa_machine_init_register_types(void) +{ + type_register_static(&machine_hppa_machine_init_typeinfo); +} + +type_init(machine_hppa_machine_init_register_types) diff --git a/target/hppa/cpu.c b/target/hppa/cpu.c index 23eb254228..37b763fca0 100644 --- a/target/hppa/cpu.c +++ b/target/hppa/cpu.c @@ -62,7 +62,7 @@ static void hppa_cpu_synchronize_from_tb(CPUState *cs, static bool hppa_cpu_has_work(CPUState *cs) { - return cs->interrupt_request & CPU_INTERRUPT_HARD; + return cs->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_NMI); } static void hppa_cpu_disas_set_info(CPUState *cs, disassemble_info *info) diff --git a/target/hppa/cpu.h b/target/hppa/cpu.h index 45fd338b02..93c119532a 100644 --- a/target/hppa/cpu.h +++ b/target/hppa/cpu.h @@ -69,6 +69,11 @@ #define EXCP_SYSCALL 30 #define EXCP_SYSCALL_LWS 31 +/* Emulated hardware TOC button */ +#define EXCP_TOC 32 /* TOC = Transfer of control (NMI) */ + +#define CPU_INTERRUPT_NMI CPU_INTERRUPT_TGT_EXT_3 /* TOC */ + /* Taken from Linux kernel: arch/parisc/include/asm/psw.h */ #define PSW_I 0x00000001 #define PSW_D 0x00000002 diff --git a/target/hppa/helper.h b/target/hppa/helper.h index 0a629ffa7c..fe8a9ce493 100644 --- a/target/hppa/helper.h +++ b/target/hppa/helper.h @@ -80,6 +80,7 @@ DEF_HELPER_FLAGS_0(read_interval_timer, TCG_CALL_NO_RWG, tr) #ifndef CONFIG_USER_ONLY DEF_HELPER_1(halt, noreturn, env) DEF_HELPER_1(reset, noreturn, env) +DEF_HELPER_1(getshadowregs, void, env) DEF_HELPER_1(rfi, void, env) DEF_HELPER_1(rfi_r, void, env) DEF_HELPER_FLAGS_2(write_interval_timer, TCG_CALL_NO_RWG, void, env, tr) diff --git a/target/hppa/insns.decode b/target/hppa/insns.decode index d4eefc0d48..c7a7e997f9 100644 --- a/target/hppa/insns.decode +++ b/target/hppa/insns.decode @@ -111,6 +111,7 @@ rfi_r 000000 ----- ----- --- 01100101 00000 # They are allocated from the unassigned instruction space. halt 1111 1111 1111 1101 1110 1010 1101 0000 reset 1111 1111 1111 1101 1110 1010 1101 0001 +getshadowregs 1111 1111 1111 1101 1110 1010 1101 0010 #### # Memory Management diff --git a/target/hppa/int_helper.c b/target/hppa/int_helper.c index 13073ae2bd..f599dccfff 100644 --- a/target/hppa/int_helper.c +++ b/target/hppa/int_helper.c @@ -23,6 +23,7 @@ #include "cpu.h" #include "exec/helper-proto.h" #include "hw/core/cpu.h" +#include "hw/hppa/hppa_hardware.h" #ifndef CONFIG_USER_ONLY static void eval_interrupt(HPPACPU *cpu) @@ -181,7 +182,14 @@ void hppa_cpu_do_interrupt(CPUState *cs) } /* step 7 */ - env->iaoq_f = env->cr[CR_IVA] + 32 * i; + if (i == EXCP_TOC) { + env->iaoq_f = FIRMWARE_START; + /* help SeaBIOS and provide iaoq_b and iasq_back in shadow regs */ + env->gr[24] = env->cr_back[0]; + env->gr[25] = env->cr_back[1]; + } else { + env->iaoq_f = env->cr[CR_IVA] + 32 * i; + } env->iaoq_b = env->iaoq_f + 4; env->iasq_f = 0; env->iasq_b = 0; @@ -219,6 +227,7 @@ void hppa_cpu_do_interrupt(CPUState *cs) [EXCP_PER_INTERRUPT] = "performance monitor interrupt", [EXCP_SYSCALL] = "syscall", [EXCP_SYSCALL_LWS] = "syscall-lws", + [EXCP_TOC] = "TOC (transfer of control)", }; static int count; const char *name = NULL; @@ -248,6 +257,14 @@ bool hppa_cpu_exec_interrupt(CPUState *cs, int interrupt_request) HPPACPU *cpu = HPPA_CPU(cs); CPUHPPAState *env = &cpu->env; + if (interrupt_request & CPU_INTERRUPT_NMI) { + /* Raise TOC (NMI) interrupt */ + cpu_reset_interrupt(cs, CPU_INTERRUPT_NMI); + cs->exception_index = EXCP_TOC; + hppa_cpu_do_interrupt(cs); + return true; + } + /* If interrupts are requested and enabled, raise them. */ if ((env->psw & PSW_I) && (interrupt_request & CPU_INTERRUPT_HARD)) { cs->exception_index = EXCP_EXT_INTERRUPT; diff --git a/target/hppa/op_helper.c b/target/hppa/op_helper.c index 1b86557d5d..b0dec4ebf4 100644 --- a/target/hppa/op_helper.c +++ b/target/hppa/op_helper.c @@ -694,7 +694,7 @@ void HELPER(rfi)(CPUHPPAState *env) cpu_hppa_put_psw(env, env->cr[CR_IPSW]); } -void HELPER(rfi_r)(CPUHPPAState *env) +void HELPER(getshadowregs)(CPUHPPAState *env) { env->gr[1] = env->shadow[0]; env->gr[8] = env->shadow[1]; @@ -703,6 +703,11 @@ void HELPER(rfi_r)(CPUHPPAState *env) env->gr[17] = env->shadow[4]; env->gr[24] = env->shadow[5]; env->gr[25] = env->shadow[6]; +} + +void HELPER(rfi_r)(CPUHPPAState *env) +{ + helper_getshadowregs(env); helper_rfi(env); } #endif diff --git a/target/hppa/translate.c b/target/hppa/translate.c index c6195590f8..5c0b1eb274 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -2393,6 +2393,16 @@ static bool trans_reset(DisasContext *ctx, arg_reset *a) #endif } +static bool trans_getshadowregs(DisasContext *ctx, arg_getshadowregs *a) +{ + CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR); +#ifndef CONFIG_USER_ONLY + nullify_over(ctx); + gen_helper_getshadowregs(cpu_env); + return nullify_end(ctx); +#endif +} + static bool trans_nop_addrx(DisasContext *ctx, arg_ldst *a) { if (a->m) { From 3b21d998a12b380c590f1005343ab95e2d6a7669 Mon Sep 17 00:00:00 2001 From: Sven Schnelle Date: Fri, 21 Jan 2022 23:16:19 +0100 Subject: [PATCH 225/460] hw/display/artist: rewrite vram access mode handling When writing this code it was assumed that register 0x118000 is the buffer access mode for color map accesses. It turned out that this is wrong. Instead register 0x118000 sets both src and dst buffer access mode at the same time. This required a larger rewrite of the code. The good thing is that both the linear framebuffer and the register based vram access can now be combined into one function. This makes the linux 'stifb' framebuffer work, and both HP-UX 10.20 and HP-UX 11.11 are still working. Signed-off-by: Sven Schnelle Signed-off-by: Helge Deller Cc: qemu-stable@nongnu.org Signed-off-by: Helge Deller --- hw/display/artist.c | 416 ++++++++++++++++------------------------ hw/display/trace-events | 8 +- 2 files changed, 166 insertions(+), 258 deletions(-) diff --git a/hw/display/artist.c b/hw/display/artist.c index 21b7fd1b44..442bdbc130 100644 --- a/hw/display/artist.c +++ b/hw/display/artist.c @@ -91,7 +91,6 @@ struct ARTISTState { uint32_t reg_300208; uint32_t reg_300218; - uint32_t cmap_bm_access; uint32_t dst_bm_access; uint32_t src_bm_access; uint32_t control_plane; @@ -134,7 +133,7 @@ typedef enum { PATTERN_LINE_START = 0x100ecc, LINE_SIZE = 0x100e04, LINE_END = 0x100e44, - CMAP_BM_ACCESS = 0x118000, + DST_SRC_BM_ACCESS = 0x118000, DST_BM_ACCESS = 0x118004, SRC_BM_ACCESS = 0x118008, CONTROL_PLANE = 0x11800c, @@ -176,7 +175,7 @@ static const char *artist_reg_name(uint64_t addr) REG_NAME(TRANSFER_DATA); REG_NAME(CONTROL_PLANE); REG_NAME(IMAGE_BITMAP_OP); - REG_NAME(CMAP_BM_ACCESS); + REG_NAME(DST_SRC_BM_ACCESS); REG_NAME(DST_BM_ACCESS); REG_NAME(SRC_BM_ACCESS); REG_NAME(CURSOR_POS); @@ -222,40 +221,14 @@ static void artist_invalidate_lines(struct vram_buffer *buf, } } -static int vram_write_pix_per_transfer(ARTISTState *s) -{ - if (s->cmap_bm_access) { - return 1 << ((s->cmap_bm_access >> 27) & 0x0f); - } else { - return 1 << ((s->dst_bm_access >> 27) & 0x0f); - } -} - -static int vram_pixel_length(ARTISTState *s) -{ - if (s->cmap_bm_access) { - return (s->cmap_bm_access >> 24) & 0x07; - } else { - return (s->dst_bm_access >> 24) & 0x07; - } -} - static int vram_write_bufidx(ARTISTState *s) { - if (s->cmap_bm_access) { - return (s->cmap_bm_access >> 12) & 0x0f; - } else { - return (s->dst_bm_access >> 12) & 0x0f; - } + return (s->dst_bm_access >> 12) & 0x0f; } static int vram_read_bufidx(ARTISTState *s) { - if (s->cmap_bm_access) { - return (s->cmap_bm_access >> 12) & 0x0f; - } else { - return (s->src_bm_access >> 12) & 0x0f; - } + return (s->src_bm_access >> 12) & 0x0f; } static struct vram_buffer *vram_read_buffer(ARTISTState *s) @@ -352,130 +325,6 @@ static void artist_invalidate_cursor(ARTISTState *s) y, s->cursor_height); } -static void vram_bit_write(ARTISTState *s, int posy, bool incr_x, - int size, uint32_t data) -{ - struct vram_buffer *buf; - uint32_t vram_bitmask = s->vram_bitmask; - int mask, i, pix_count, pix_length; - unsigned int posx, offset, width; - uint8_t *data8, *p; - - pix_count = vram_write_pix_per_transfer(s); - pix_length = vram_pixel_length(s); - - buf = vram_write_buffer(s); - width = buf->width; - - if (s->cmap_bm_access) { - offset = s->vram_pos; - } else { - posx = ADDR_TO_X(s->vram_pos >> 2); - posy += ADDR_TO_Y(s->vram_pos >> 2); - offset = posy * width + posx; - } - - if (!buf->size || offset >= buf->size) { - return; - } - - p = buf->data; - - if (pix_count > size * 8) { - pix_count = size * 8; - } - - switch (pix_length) { - case 0: - if (s->image_bitmap_op & 0x20000000) { - data &= vram_bitmask; - } - - for (i = 0; i < pix_count; i++) { - uint32_t off = offset + pix_count - 1 - i; - if (off < buf->size) { - artist_rop8(s, buf, off, - (data & 1) ? (s->plane_mask >> 24) : 0); - } - data >>= 1; - } - memory_region_set_dirty(&buf->mr, offset, pix_count); - break; - - case 3: - if (s->cmap_bm_access) { - if (offset + 3 < buf->size) { - *(uint32_t *)(p + offset) = data; - } - break; - } - data8 = (uint8_t *)&data; - - for (i = 3; i >= 0; i--) { - if (!(s->image_bitmap_op & 0x20000000) || - s->vram_bitmask & (1 << (28 + i))) { - uint32_t off = offset + 3 - i; - if (off < buf->size) { - artist_rop8(s, buf, off, data8[ROP8OFF(i)]); - } - } - } - memory_region_set_dirty(&buf->mr, offset, 3); - break; - - case 6: - switch (size) { - default: - case 4: - vram_bitmask = s->vram_bitmask; - break; - - case 2: - vram_bitmask = s->vram_bitmask >> 16; - break; - - case 1: - vram_bitmask = s->vram_bitmask >> 24; - break; - } - - for (i = 0; i < pix_count && offset + i < buf->size; i++) { - mask = 1 << (pix_count - 1 - i); - - if (!(s->image_bitmap_op & 0x20000000) || - (vram_bitmask & mask)) { - if (data & mask) { - artist_rop8(s, buf, offset + i, s->fg_color); - } else { - if (!(s->image_bitmap_op & 0x10000002)) { - artist_rop8(s, buf, offset + i, s->bg_color); - } - } - } - } - memory_region_set_dirty(&buf->mr, offset, pix_count); - break; - - default: - qemu_log_mask(LOG_UNIMP, "%s: unknown pixel length %d\n", - __func__, pix_length); - break; - } - - if (incr_x) { - if (s->cmap_bm_access) { - s->vram_pos += 4; - } else { - s->vram_pos += pix_count << 2; - } - } - - if (vram_write_bufidx(s) == ARTIST_BUFFER_CURSOR1 || - vram_write_bufidx(s) == ARTIST_BUFFER_CURSOR2) { - artist_invalidate_cursor(s); - } -} - static void block_move(ARTISTState *s, unsigned int source_x, unsigned int source_y, unsigned int dest_x, unsigned int dest_y, @@ -860,6 +709,151 @@ static void combine_write_reg(hwaddr addr, uint64_t val, int size, void *out) } } +static void artist_vram_write4(ARTISTState *s, struct vram_buffer *buf, + uint32_t offset, uint32_t data) +{ + int i; + int mask = s->vram_bitmask >> 28; + + for (i = 0; i < 4; i++) { + if (!(s->image_bitmap_op & 0x20000000) || (mask & 8)) { + artist_rop8(s, buf, offset + i, data >> 24); + data <<= 8; + mask <<= 1; + } + } + memory_region_set_dirty(&buf->mr, offset, 3); +} + +static void artist_vram_write32(ARTISTState *s, struct vram_buffer *buf, + uint32_t offset, int size, uint32_t data, + int fg, int bg) +{ + uint32_t mask, vram_bitmask = s->vram_bitmask >> ((4 - size) * 8); + int i, pix_count = size * 8; + + for (i = 0; i < pix_count && offset + i < buf->size; i++) { + mask = 1 << (pix_count - 1 - i); + + if (!(s->image_bitmap_op & 0x20000000) || (vram_bitmask & mask)) { + if (data & mask) { + artist_rop8(s, buf, offset + i, fg); + } else { + if (!(s->image_bitmap_op & 0x10000002)) { + artist_rop8(s, buf, offset + i, bg); + } + } + } + } + memory_region_set_dirty(&buf->mr, offset, pix_count); +} + +static int get_vram_offset(ARTISTState *s, struct vram_buffer *buf, + int pos, int posy) +{ + unsigned int posx, width; + + width = buf->width; + posx = ADDR_TO_X(pos); + posy += ADDR_TO_Y(pos); + return posy * width + posx; +} + +static int vram_bit_write(ARTISTState *s, uint32_t pos, int posy, + uint32_t data, int size) +{ + struct vram_buffer *buf = vram_write_buffer(s); + + switch (s->dst_bm_access >> 16) { + case 0x3ba0: + case 0xbbe0: + artist_vram_write4(s, buf, pos, bswap32(data)); + pos += 4; + break; + + case 0x1360: /* linux */ + artist_vram_write4(s, buf, get_vram_offset(s, buf, pos, posy), data); + pos += 4; + break; + + case 0x13a0: + artist_vram_write4(s, buf, get_vram_offset(s, buf, pos >> 2, posy), + data); + pos += 16; + break; + + case 0x2ea0: + artist_vram_write32(s, buf, get_vram_offset(s, buf, pos >> 2, posy), + size, data, s->fg_color, s->bg_color); + pos += 4; + break; + + case 0x28a0: + artist_vram_write32(s, buf, get_vram_offset(s, buf, pos >> 2, posy), + size, data, 1, 0); + pos += 4; + break; + + default: + qemu_log_mask(LOG_UNIMP, "%s: unknown dst bm access %08x\n", + __func__, s->dst_bm_access); + break; + } + + if (vram_write_bufidx(s) == ARTIST_BUFFER_CURSOR1 || + vram_write_bufidx(s) == ARTIST_BUFFER_CURSOR2) { + artist_invalidate_cursor(s); + } + return pos; +} + +static void artist_vram_write(void *opaque, hwaddr addr, uint64_t val, + unsigned size) +{ + ARTISTState *s = opaque; + s->vram_char_y = 0; + trace_artist_vram_write(size, addr, val); + vram_bit_write(opaque, addr, 0, val, size); +} + +static uint64_t artist_vram_read(void *opaque, hwaddr addr, unsigned size) +{ + ARTISTState *s = opaque; + struct vram_buffer *buf; + unsigned int offset; + uint64_t val; + + buf = vram_read_buffer(s); + if (!buf->size) { + return 0; + } + + offset = get_vram_offset(s, buf, addr >> 2, 0); + + if (offset > buf->size) { + return 0; + } + + switch (s->src_bm_access >> 16) { + case 0x3ba0: + val = *(uint32_t *)(buf->data + offset); + break; + + case 0x13a0: + case 0x2ea0: + val = bswap32(*(uint32_t *)(buf->data + offset)); + break; + + default: + qemu_log_mask(LOG_UNIMP, "%s: unknown src bm access %08x\n", + __func__, s->dst_bm_access); + val = -1ULL; + break; + } + trace_artist_vram_read(size, addr, val); + return val; +} + static void artist_reg_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) { @@ -886,12 +880,12 @@ static void artist_reg_write(void *opaque, hwaddr addr, uint64_t val, break; case VRAM_WRITE_INCR_Y: - vram_bit_write(s, s->vram_char_y++, false, size, val); + vram_bit_write(s, s->vram_pos, s->vram_char_y++, val, size); break; case VRAM_WRITE_INCR_X: case VRAM_WRITE_INCR_X2: - vram_bit_write(s, s->vram_char_y, true, size, val); + s->vram_pos = vram_bit_write(s, s->vram_pos, s->vram_char_y, val, size); break; case VRAM_IDX: @@ -993,18 +987,17 @@ static void artist_reg_write(void *opaque, hwaddr addr, uint64_t val, combine_write_reg(addr, val, size, &s->plane_mask); break; - case CMAP_BM_ACCESS: - combine_write_reg(addr, val, size, &s->cmap_bm_access); + case DST_SRC_BM_ACCESS: + combine_write_reg(addr, val, size, &s->dst_bm_access); + combine_write_reg(addr, val, size, &s->src_bm_access); break; case DST_BM_ACCESS: combine_write_reg(addr, val, size, &s->dst_bm_access); - s->cmap_bm_access = 0; break; case SRC_BM_ACCESS: combine_write_reg(addr, val, size, &s->src_bm_access); - s->cmap_bm_access = 0; break; case CONTROL_PLANE: @@ -1152,98 +1145,6 @@ static uint64_t artist_reg_read(void *opaque, hwaddr addr, unsigned size) return val; } -static void artist_vram_write(void *opaque, hwaddr addr, uint64_t val, - unsigned size) -{ - ARTISTState *s = opaque; - struct vram_buffer *buf; - unsigned int posy, posx; - unsigned int offset; - trace_artist_vram_write(size, addr, val); - - if (s->cmap_bm_access) { - buf = &s->vram_buffer[ARTIST_BUFFER_CMAP]; - if (addr + 3 < buf->size) { - *(uint32_t *)(buf->data + addr) = val; - } - return; - } - - buf = vram_write_buffer(s); - posy = ADDR_TO_Y(addr >> 2); - posx = ADDR_TO_X(addr >> 2); - - if (!buf->size) { - return; - } - - if (posy > buf->height || posx > buf->width) { - return; - } - - offset = posy * buf->width + posx; - if (offset >= buf->size) { - return; - } - - switch (size) { - case 4: - if (offset + 3 < buf->size) { - *(uint32_t *)(buf->data + offset) = be32_to_cpu(val); - memory_region_set_dirty(&buf->mr, offset, 4); - } - break; - case 2: - if (offset + 1 < buf->size) { - *(uint16_t *)(buf->data + offset) = be16_to_cpu(val); - memory_region_set_dirty(&buf->mr, offset, 2); - } - break; - case 1: - if (offset < buf->size) { - *(uint8_t *)(buf->data + offset) = val; - memory_region_set_dirty(&buf->mr, offset, 1); - } - break; - default: - break; - } -} - -static uint64_t artist_vram_read(void *opaque, hwaddr addr, unsigned size) -{ - ARTISTState *s = opaque; - struct vram_buffer *buf; - uint64_t val; - unsigned int posy, posx; - - if (s->cmap_bm_access) { - buf = &s->vram_buffer[ARTIST_BUFFER_CMAP]; - val = 0; - if (addr < buf->size && addr + 3 < buf->size) { - val = *(uint32_t *)(buf->data + addr); - } - trace_artist_vram_read(size, addr, 0, 0, val); - return val; - } - - buf = vram_read_buffer(s); - if (!buf->size) { - return 0; - } - - posy = ADDR_TO_Y(addr >> 2); - posx = ADDR_TO_X(addr >> 2); - - if (posy > buf->height || posx > buf->width) { - return 0; - } - - val = cpu_to_be32(*(uint32_t *)(buf->data + posy * buf->width + posx)); - trace_artist_vram_read(size, addr, posx, posy, val); - return val; -} - static const MemoryRegionOps artist_reg_ops = { .read = artist_reg_read, .write = artist_reg_write, @@ -1410,6 +1311,14 @@ static void artist_realizefn(DeviceState *dev, Error **errp) s->cursor_height = 32; s->cursor_width = 32; + /* + * These two registers are not initialized by seabios's STI implementation. + * Initialize them here to sane values so artist also works with older + * (not-fixed) seabios versions. + */ + s->image_bitmap_op = 0x23000300; + s->plane_mask = 0xff; + s->con = graphic_console_init(dev, 0, &artist_ops, s); qemu_console_resize(s->con, s->width, s->height); } @@ -1450,7 +1359,6 @@ static const VMStateDescription vmstate_artist = { VMSTATE_UINT32(reg_300200, ARTISTState), VMSTATE_UINT32(reg_300208, ARTISTState), VMSTATE_UINT32(reg_300218, ARTISTState), - VMSTATE_UINT32(cmap_bm_access, ARTISTState), VMSTATE_UINT32(dst_bm_access, ARTISTState), VMSTATE_UINT32(src_bm_access, ARTISTState), VMSTATE_UINT32(control_plane, ARTISTState), diff --git a/hw/display/trace-events b/hw/display/trace-events index 3a7a2c957f..4a687d1b8e 100644 --- a/hw/display/trace-events +++ b/hw/display/trace-events @@ -140,10 +140,10 @@ ati_mm_read(unsigned int size, uint64_t addr, const char *name, uint64_t val) "% ati_mm_write(unsigned int size, uint64_t addr, const char *name, uint64_t val) "%u 0x%"PRIx64 " %s <- 0x%"PRIx64 # artist.c -artist_reg_read(unsigned int size, uint64_t addr, const char *name, uint64_t val) "%u 0x%"PRIx64 "%s -> 0x%"PRIx64 -artist_reg_write(unsigned int size, uint64_t addr, const char *name, uint64_t val) "%u 0x%"PRIx64 "%s <- 0x%"PRIx64 -artist_vram_read(unsigned int size, uint64_t addr, int posx, int posy, uint64_t val) "%u 0x%"PRIx64 " %ux%u-> 0x%"PRIx64 -artist_vram_write(unsigned int size, uint64_t addr, uint64_t val) "%u 0x%"PRIx64 " <- 0x%"PRIx64 +artist_reg_read(unsigned int size, uint64_t addr, const char *name, uint64_t val) "%u 0x%"PRIx64 "%s -> 0x%08"PRIx64 +artist_reg_write(unsigned int size, uint64_t addr, const char *name, uint64_t val) "%u 0x%"PRIx64 "%s <- 0x%08"PRIx64 +artist_vram_read(unsigned int size, uint64_t addr, uint64_t val) "%u 0x%08"PRIx64 " -> 0x%08"PRIx64 +artist_vram_write(unsigned int size, uint64_t addr, uint64_t val) "%u 0x%08"PRIx64 " <- 0x%08"PRIx64 artist_fill_window(unsigned int start_x, unsigned int start_y, unsigned int width, unsigned int height, uint32_t op, uint32_t ctlpln) "start=%ux%u length=%ux%u op=0x%08x ctlpln=0x%08x" artist_block_move(unsigned int start_x, unsigned int start_y, unsigned int dest_x, unsigned int dest_y, unsigned int width, unsigned int height) "source %ux%u -> dest %ux%u size %ux%u" artist_draw_line(unsigned int start_x, unsigned int start_y, unsigned int end_x, unsigned int end_y) "%ux%u %ux%u" From 3615cea4714f94d1db61bd6618a3a66a6b014f9d Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Wed, 5 Jan 2022 23:06:12 +0100 Subject: [PATCH 226/460] hw/display/artist: Mouse cursor fixes for HP-UX This patch fix the behaviour and positioning of the X11 mouse cursor in HP-UX. The current code missed to subtract the offset of the CURSOR_CTRL register from the current mouse cursor position. The HP-UX graphics driver stores in this register the offset of the mouse graphics compared to the current cursor position. Without this adjustment the mouse behaves strange at the screen borders. Additionally, depending on the HP-UX version, the mouse cursor position in the cursor_pos register reports different values. To accommodate this track the current min and max reported values and auto-adjust at runtime. With this fix the mouse now behaves as expected on HP-UX 10 and 11. Signed-off-by: Helge Deller Cc: qemu-stable@nongnu.org Signed-off-by: Helge Deller --- hw/display/artist.c | 42 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 8 deletions(-) diff --git a/hw/display/artist.c b/hw/display/artist.c index 442bdbc130..8a9fa482d0 100644 --- a/hw/display/artist.c +++ b/hw/display/artist.c @@ -80,6 +80,7 @@ struct ARTISTState { uint32_t line_pattern_skip; uint32_t cursor_pos; + uint32_t cursor_cntrl; uint32_t cursor_height; uint32_t cursor_width; @@ -301,19 +302,42 @@ static void artist_get_cursor_pos(ARTISTState *s, int *x, int *y) { /* * Don't know whether these magic offset values are configurable via - * some register. They are the same for all resolutions, so don't - * bother about it. + * some register. They seem to be the same for all resolutions. + * The cursor values provided in the registers are: + * X-value: -295 (for HP-UX 11) and 338 (for HP-UX 10.20) up to 2265 + * Y-value: 1146 down to 0 + * The emulated Artist graphic is like a CRX graphic, and as such + * it's usually fixed at 1280x1024 pixels. + * Because of the maximum Y-value of 1146 you can not choose a higher + * vertical resolution on HP-UX (unless you disable the mouse). */ - *y = 0x47a - artist_get_y(s->cursor_pos); - *x = ((artist_get_x(s->cursor_pos) - 338) / 2); + static int offset = 338; + int lx; + + /* ignore if uninitialized */ + if (s->cursor_pos == 0) { + *x = *y = 0; + return; + } + + lx = artist_get_x(s->cursor_pos); + if (lx < offset) + offset = lx; + *x = (lx - offset) / 2; + + *y = 1146 - artist_get_y(s->cursor_pos); + + /* subtract cursor offset from cursor control register */ + *x -= (s->cursor_cntrl & 0xf0) >> 4; + *y -= (s->cursor_cntrl & 0x0f); if (*x > s->width) { - *x = 0; + *x = s->width; } if (*y > s->height) { - *y = 0; + *y = s->height; } } @@ -1027,6 +1051,7 @@ static void artist_reg_write(void *opaque, hwaddr addr, uint64_t val, break; case CURSOR_CTRL: + combine_write_reg(addr, val, size, &s->cursor_cntrl); break; case IMAGE_BITMAP_OP: @@ -1331,8 +1356,8 @@ static int vmstate_artist_post_load(void *opaque, int version_id) static const VMStateDescription vmstate_artist = { .name = "artist", - .version_id = 1, - .minimum_version_id = 1, + .version_id = 2, + .minimum_version_id = 2, .post_load = vmstate_artist_post_load, .fields = (VMStateField[]) { VMSTATE_UINT16(height, ARTISTState), @@ -1352,6 +1377,7 @@ static const VMStateDescription vmstate_artist = { VMSTATE_UINT32(line_end, ARTISTState), VMSTATE_UINT32(line_xy, ARTISTState), VMSTATE_UINT32(cursor_pos, ARTISTState), + VMSTATE_UINT32(cursor_cntrl, ARTISTState), VMSTATE_UINT32(cursor_height, ARTISTState), VMSTATE_UINT32(cursor_width, ARTISTState), VMSTATE_UINT32(plane_mask, ARTISTState), From d449eee3af37937f788c02ad88f2caa8bbfb19aa Mon Sep 17 00:00:00 2001 From: Sven Schnelle Date: Sun, 23 Jan 2022 16:47:47 +0100 Subject: [PATCH 227/460] hw/display/artist: Fix draw_line() artefacts The draw_line() function left artefacts on the screen because it was using the x/y variables which were incremented in the loop before. Fix it by using the unmodified x1/x2 variables instead. Signed-off-by: Sven Schnelle Signed-off-by: Helge Deller Cc: qemu-stable@nongnu.org Signed-off-by: Helge Deller --- hw/display/artist.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/hw/display/artist.c b/hw/display/artist.c index 8a9fa482d0..1d877998b9 100644 --- a/hw/display/artist.c +++ b/hw/display/artist.c @@ -553,10 +553,11 @@ static void draw_line(ARTISTState *s, } x++; } while (x <= x2 && (max_pix == -1 || --max_pix > 0)); + if (c1) - artist_invalidate_lines(buf, x, dy+1); + artist_invalidate_lines(buf, x1, x2 - x1); else - artist_invalidate_lines(buf, y, dx+1); + artist_invalidate_lines(buf, y1 > y2 ? y2 : y1, x2 - x1); } static void draw_line_pattern_start(ARTISTState *s) From fa73e6e4ca1a93c5bbf9d05fb2a25736ab810b35 Mon Sep 17 00:00:00 2001 From: John Snow Date: Mon, 31 Jan 2022 23:11:31 -0500 Subject: [PATCH 228/460] python/aqmp: Fix negotiation with pre-"oob" QEMU QEMU versions prior to the "oob" capability *also* can't accept the "enable" keyword argument at all. Fix the handshake process with older QEMU versions. Signed-off-by: John Snow Reviewed-by: Hanna Reitz Reviewed-by: Kevin Wolf Message-id: 20220201041134.1237016-2-jsnow@redhat.com Signed-off-by: John Snow --- python/qemu/aqmp/qmp_client.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/qemu/aqmp/qmp_client.py b/python/qemu/aqmp/qmp_client.py index f1a845cc82..90a8737f03 100644 --- a/python/qemu/aqmp/qmp_client.py +++ b/python/qemu/aqmp/qmp_client.py @@ -292,9 +292,9 @@ class QMPClient(AsyncProtocol[Message], Events): """ self.logger.debug("Negotiating capabilities ...") - arguments: Dict[str, List[str]] = {'enable': []} + arguments: Dict[str, List[str]] = {} if self._greeting and 'oob' in self._greeting.QMP.capabilities: - arguments['enable'].append('oob') + arguments.setdefault('enable', []).append('oob') msg = self.make_execute_msg('qmp_capabilities', arguments=arguments) # It's not safe to use execute() here, because the reader/writers From 50465f94d211beabfbfc80e4f85ec4fad0757570 Mon Sep 17 00:00:00 2001 From: John Snow Date: Mon, 31 Jan 2022 23:11:32 -0500 Subject: [PATCH 229/460] python/machine: raise VMLaunchFailure exception from launch() This allows us to pack in some extra information about the failure, which guarantees that if the caller did not *intentionally* cause a failure (by capturing this Exception), some pretty good clues will be printed at the bottom of the traceback information. This will help make failures in the event of a non-negative return code more obvious when they go unhandled; the current behavior in _post_shutdown() is to print a warning message only in the event of signal-based terminations (for negative return codes). (Note: In Python, catching BaseException instead of Exception catches a broader array of Exception events, including SystemExit and KeyboardInterrupt. We do not want to "wrap" such exceptions as a VMLaunchFailure, because that will 'downgrade' the exception from a BaseException to a regular Exception. We do, however, want to perform cleanup in either case, so catch on the broadest scope and wrap-and-re-raise only in the more targeted scope.) Signed-off-by: John Snow Reviewed-by: Hanna Reitz Reviewed-by: Kevin Wolf Message-id: 20220201041134.1237016-3-jsnow@redhat.com Signed-off-by: John Snow --- python/qemu/machine/machine.py | 45 ++++++++++++++++++++--- tests/qemu-iotests/tests/mirror-top-perms | 3 +- 2 files changed, 40 insertions(+), 8 deletions(-) diff --git a/python/qemu/machine/machine.py b/python/qemu/machine/machine.py index 67ab06ca2b..a5972fab4d 100644 --- a/python/qemu/machine/machine.py +++ b/python/qemu/machine/machine.py @@ -74,6 +74,35 @@ class QEMUMachineAddDeviceError(QEMUMachineError): """ +class VMLaunchFailure(QEMUMachineError): + """ + Exception raised when a VM launch was attempted, but failed. + """ + def __init__(self, exitcode: Optional[int], + command: str, output: Optional[str]): + super().__init__(exitcode, command, output) + self.exitcode = exitcode + self.command = command + self.output = output + + def __str__(self) -> str: + ret = '' + if self.__cause__ is not None: + name = type(self.__cause__).__name__ + reason = str(self.__cause__) + if reason: + ret += f"{name}: {reason}" + else: + ret += f"{name}" + ret += '\n' + + if self.exitcode is not None: + ret += f"\tExit code: {self.exitcode}\n" + ret += f"\tCommand: {self.command}\n" + ret += f"\tOutput: {self.output}\n" + return ret + + class AbnormalShutdown(QEMUMachineError): """ Exception raised when a graceful shutdown was requested, but not performed. @@ -397,7 +426,7 @@ class QEMUMachine: try: self._launch() - except: + except BaseException as exc: # We may have launched the process but it may # have exited before we could connect via QMP. # Assume the VM didn't launch or is exiting. @@ -408,11 +437,15 @@ class QEMUMachine: else: self._post_shutdown() - LOG.debug('Error launching VM') - if self._qemu_full_args: - LOG.debug('Command: %r', ' '.join(self._qemu_full_args)) - if self._iolog: - LOG.debug('Output: %r', self._iolog) + if isinstance(exc, Exception): + raise VMLaunchFailure( + exitcode=self.exitcode(), + command=' '.join(self._qemu_full_args), + output=self._iolog + ) from exc + + # Don't wrap 'BaseException'; doing so would downgrade + # that exception. However, we still want to clean up. raise def _launch(self) -> None: diff --git a/tests/qemu-iotests/tests/mirror-top-perms b/tests/qemu-iotests/tests/mirror-top-perms index 0a51a613f3..b5849978c4 100755 --- a/tests/qemu-iotests/tests/mirror-top-perms +++ b/tests/qemu-iotests/tests/mirror-top-perms @@ -21,7 +21,6 @@ import os -from qemu.aqmp import ConnectError from qemu.machine import machine from qemu.qmp import QMPConnectError @@ -107,7 +106,7 @@ class TestMirrorTopPerms(iotests.QMPTestCase): self.vm_b.launch() print('ERROR: VM B launched successfully, ' 'this should not have happened') - except (QMPConnectError, ConnectError): + except (QMPConnectError, machine.VMLaunchFailure): assert 'Is another process using the image' in self.vm_b.get_log() result = self.vm.qmp('block-job-cancel', From 74a1505d279897d2a448c876820a33cbe1f0f22e Mon Sep 17 00:00:00 2001 From: John Snow Date: Mon, 31 Jan 2022 23:11:33 -0500 Subject: [PATCH 230/460] python: upgrade mypy to 0.780 We need a slightly newer version of mypy in order to use some features of the asyncio server functions in the next commit. (Note: pipenv is not really suited to upgrading individual packages; I need to replace this tool with something better for the task. For now, the miscellaneous updates not related to the mypy upgrade are simply beyond my control. It's on my list to take care of soon.) Signed-off-by: John Snow Reviewed-by: Kevin Wolf Message-id: 20220201041134.1237016-4-jsnow@redhat.com Signed-off-by: John Snow --- python/Pipfile.lock | 66 ++++++++++++++++++++++++++------------------- python/setup.cfg | 2 +- 2 files changed, 40 insertions(+), 28 deletions(-) diff --git a/python/Pipfile.lock b/python/Pipfile.lock index d2a7dbd88b..ce46404ce0 100644 --- a/python/Pipfile.lock +++ b/python/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "784b327272db32403d5a488507853b5afba850ba26a5948e5b6a90c1baef2d9c" + "sha256": "f1a25654d884a5b450e38d78b1f2e3ebb9073e421cc4358d4bbb83ac251a5670" }, "pipfile-spec": 6, "requires": { @@ -34,7 +34,7 @@ "sha256:09bdb456e02564731f8b5957cdd0c98a7f01d2db5e90eb1d794c353c28bfd705", "sha256:6a8a51f64dae307f6e0c9db752b66a7951e282389d8362cc1d39a56f3feeb31d" ], - "markers": "python_version ~= '3.6'", + "index": "pypi", "version": "==2.6.0" }, "avocado-framework": { @@ -50,6 +50,7 @@ "sha256:106fef6dc37dd8c0e2c0a60d3fca3e77460a48907f335fa28420463a6f799736", "sha256:23e223426b28491b1ced97dc3bbe183027419dfc7982b4fa2f05d5f3ff10711c" ], + "index": "pypi", "version": "==0.3.2" }, "filelock": { @@ -57,6 +58,7 @@ "sha256:18d82244ee114f543149c66a6e0c14e9c4f8a1044b5cdaadd0f82159d6a6ff59", "sha256:929b7d63ec5b7d6b71b0fa5ac14e030b3f70b75747cef1b10da9b879fef15836" ], + "index": "pypi", "version": "==3.0.12" }, "flake8": { @@ -88,7 +90,7 @@ "sha256:54161657e8ffc76596c4ede7080ca68cb02962a2e074a2586b695a93a925d36e", "sha256:e962bff7440364183203d179d7ae9ad90cb1f2b74dcb84300e88ecc42dca3351" ], - "markers": "python_version < '3.7'", + "index": "pypi", "version": "==5.1.4" }, "isort": { @@ -124,7 +126,7 @@ "sha256:ed361bb83436f117f9917d282a456f9e5009ea12fd6de8742d1a4752c3017e93", "sha256:f5144c75445ae3ca2057faac03fda5a902eff196702b0a24daf1d6ce0650514b" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", + "index": "pypi", "version": "==1.6.0" }, "mccabe": { @@ -136,23 +138,23 @@ }, "mypy": { "hashes": [ - "sha256:15b948e1302682e3682f11f50208b726a246ab4e6c1b39f9264a8796bb416aa2", - "sha256:219a3116ecd015f8dca7b5d2c366c973509dfb9a8fc97ef044a36e3da66144a1", - "sha256:3b1fc683fb204c6b4403a1ef23f0b1fac8e4477091585e0c8c54cbdf7d7bb164", - "sha256:3beff56b453b6ef94ecb2996bea101a08f1f8a9771d3cbf4988a61e4d9973761", - "sha256:7687f6455ec3ed7649d1ae574136835a4272b65b3ddcf01ab8704ac65616c5ce", - "sha256:7ec45a70d40ede1ec7ad7f95b3c94c9cf4c186a32f6bacb1795b60abd2f9ef27", - "sha256:86c857510a9b7c3104cf4cde1568f4921762c8f9842e987bc03ed4f160925754", - "sha256:8a627507ef9b307b46a1fea9513d5c98680ba09591253082b4c48697ba05a4ae", - "sha256:8dfb69fbf9f3aeed18afffb15e319ca7f8da9642336348ddd6cab2713ddcf8f9", - "sha256:a34b577cdf6313bf24755f7a0e3f3c326d5c1f4fe7422d1d06498eb25ad0c600", - "sha256:a8ffcd53cb5dfc131850851cc09f1c44689c2812d0beb954d8138d4f5fc17f65", - "sha256:b90928f2d9eb2f33162405f32dde9f6dcead63a0971ca8a1b50eb4ca3e35ceb8", - "sha256:c56ffe22faa2e51054c5f7a3bc70a370939c2ed4de308c690e7949230c995913", - "sha256:f91c7ae919bbc3f96cd5e5b2e786b2b108343d1d7972ea130f7de27fdd547cf3" + "sha256:00cb1964a7476e871d6108341ac9c1a857d6bd20bf5877f4773ac5e9d92cd3cd", + "sha256:127de5a9b817a03a98c5ae8a0c46a20dc44442af6dcfa2ae7f96cb519b312efa", + "sha256:1f3976a945ad7f0a0727aafdc5651c2d3278e3c88dee94e2bf75cd3386b7b2f4", + "sha256:2f8c098f12b402c19b735aec724cc9105cc1a9eea405d08814eb4b14a6fb1a41", + "sha256:4ef13b619a289aa025f2273e05e755f8049bb4eaba6d703a425de37d495d178d", + "sha256:5d142f219bf8c7894dfa79ebfb7d352c4c63a325e75f10dfb4c3db9417dcd135", + "sha256:62eb5dd4ea86bda8ce386f26684f7f26e4bfe6283c9f2b6ca6d17faf704dcfad", + "sha256:64c36eb0936d0bfb7d8da49f92c18e312ad2e3ed46e5548ae4ca997b0d33bd59", + "sha256:75eed74d2faf2759f79c5f56f17388defd2fc994222312ec54ee921e37b31ad4", + "sha256:974bebe3699b9b46278a7f076635d219183da26e1a675c1f8243a69221758273", + "sha256:a5e5bb12b7982b179af513dddb06fca12285f0316d74f3964078acbfcf4c68f2", + "sha256:d31291df31bafb997952dc0a17ebb2737f802c754aed31dd155a8bfe75112c57", + "sha256:d3b4941de44341227ece1caaf5b08b23e42ad4eeb8b603219afb11e9d4cfb437", + "sha256:eadb865126da4e3c4c95bdb47fe1bb087a3e3ea14d39a3b13224b8a4d9f9a102" ], "index": "pypi", - "version": "==0.770" + "version": "==0.780" }, "mypy-extensions": { "hashes": [ @@ -166,7 +168,7 @@ "sha256:5b327ac1320dc863dca72f4514ecc086f31186744b84a230374cc1fd776feae5", "sha256:67714da7f7bc052e064859c05c595155bd1ee9f69f76557e21f051443c20947a" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "index": "pypi", "version": "==20.9" }, "pluggy": { @@ -174,7 +176,7 @@ "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0", "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "index": "pypi", "version": "==0.13.1" }, "py": { @@ -182,7 +184,7 @@ "sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3", "sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "index": "pypi", "version": "==1.10.0" }, "pycodestyle": { @@ -205,7 +207,7 @@ "sha256:a18f47b506a429f6f4b9df81bb02beab9ca21d0a5fee38ed15aef65f0545519f", "sha256:d66e804411278594d764fc69ec36ec13d9ae9147193a1740cd34d272ca383b8e" ], - "markers": "python_version >= '3.5'", + "index": "pypi", "version": "==2.9.0" }, "pylint": { @@ -221,13 +223,21 @@ "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1", "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b" ], - "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "index": "pypi", "version": "==2.4.7" }, "qemu": { "editable": true, "path": "." }, + "setuptools": { + "hashes": [ + "sha256:22c7348c6d2976a52632c67f7ab0cdf40147db7789f9aed18734643fe9cf3373", + "sha256:4ce92f1e1f8f01233ee9952c04f6b81d1e02939d6e1b488428154974a4d0783e" + ], + "markers": "python_version >= '3.6'", + "version": "==59.6.0" + }, "six": { "hashes": [ "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", @@ -294,19 +304,21 @@ "sha256:50b6f157849174217d0656f99dc82fe932884fb250826c18350e159ec6cdf342", "sha256:779383f6086d90c99ae41cf0ff39aac8a7937a9283ce0a414e5dd782f4c94a84" ], - "markers": "python_version < '3.8'", + "index": "pypi", "version": "==3.10.0.0" }, "urwid": { "hashes": [ "sha256:588bee9c1cb208d0906a9f73c613d2bd32c3ed3702012f51efe318a3f2127eae" ], + "index": "pypi", "version": "==2.1.2" }, "urwid-readline": { "hashes": [ "sha256:018020cbc864bb5ed87be17dc26b069eae2755cb29f3a9c569aac3bded1efaf4" ], + "index": "pypi", "version": "==0.13" }, "virtualenv": { @@ -314,7 +326,7 @@ "sha256:14fdf849f80dbb29a4eb6caa9875d476ee2a5cf76a5f5415fa2f1606010ab467", "sha256:2b0126166ea7c9c3661f5b8e06773d28f83322de7a3ff7d06f0aed18c9de6a76" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "index": "pypi", "version": "==20.4.7" }, "wrapt": { @@ -328,7 +340,7 @@ "sha256:3607921face881ba3e026887d8150cca609d517579abe052ac81fc5aeffdbd76", "sha256:51cb66cc54621609dd593d1787f286ee42a5c0adbb4b29abea5a63edc3e03098" ], - "markers": "python_version < '3.10'", + "index": "pypi", "version": "==3.4.1" } } diff --git a/python/setup.cfg b/python/setup.cfg index 3fb18f845d..18aea2bab3 100644 --- a/python/setup.cfg +++ b/python/setup.cfg @@ -41,7 +41,7 @@ devel = flake8 >= 3.6.0 fusepy >= 2.0.4 isort >= 5.1.2 - mypy >= 0.770 + mypy >= 0.780 pylint >= 2.8.0 tox >= 3.18.0 urwid >= 2.1.2 From b0b662bb2b340d63529672b5bdae596a6243c4d0 Mon Sep 17 00:00:00 2001 From: John Snow Date: Mon, 31 Jan 2022 23:11:34 -0500 Subject: [PATCH 231/460] python/aqmp: add socket bind step to legacy.py The synchronous QMP library would bind to the server address during __init__(). The new library delays this to the accept() call, because binding occurs inside of the call to start_[unix_]server(), which is an async method -- so it cannot happen during __init__ anymore. Python 3.7+ adds the ability to create the server (and thus the bind() call) and begin the active listening in separate steps, but we don't have that functionality in 3.6, our current minimum. Therefore ... Add a temporary workaround that allows the synchronous version of the client to bind the socket in advance, guaranteeing that there will be a UNIX socket in the filesystem ready for the QEMU client to connect to without a race condition. (Yes, it's a bit ugly. Fixing it more nicely will have to wait until our minimum Python version is 3.7+.) Signed-off-by: John Snow Reviewed-by: Kevin Wolf Message-id: 20220201041134.1237016-5-jsnow@redhat.com Signed-off-by: John Snow --- python/qemu/aqmp/legacy.py | 3 +++ python/qemu/aqmp/protocol.py | 41 +++++++++++++++++++++++++++++++++--- 2 files changed, 41 insertions(+), 3 deletions(-) diff --git a/python/qemu/aqmp/legacy.py b/python/qemu/aqmp/legacy.py index 0890f95b16..6baa5f3409 100644 --- a/python/qemu/aqmp/legacy.py +++ b/python/qemu/aqmp/legacy.py @@ -56,6 +56,9 @@ class QEMUMonitorProtocol(qemu.qmp.QEMUMonitorProtocol): self._address = address self._timeout: Optional[float] = None + if server: + self._aqmp._bind_hack(address) # pylint: disable=protected-access + _T = TypeVar('_T') def _sync( diff --git a/python/qemu/aqmp/protocol.py b/python/qemu/aqmp/protocol.py index 50e973c2f2..33358f5cd7 100644 --- a/python/qemu/aqmp/protocol.py +++ b/python/qemu/aqmp/protocol.py @@ -15,6 +15,7 @@ from asyncio import StreamReader, StreamWriter from enum import Enum from functools import wraps import logging +import socket from ssl import SSLContext from typing import ( Any, @@ -238,6 +239,9 @@ class AsyncProtocol(Generic[T]): self._runstate = Runstate.IDLE self._runstate_changed: Optional[asyncio.Event] = None + # Workaround for bind() + self._sock: Optional[socket.socket] = None + def __repr__(self) -> str: cls_name = type(self).__name__ tokens = [] @@ -427,6 +431,34 @@ class AsyncProtocol(Generic[T]): else: await self._do_connect(address, ssl) + def _bind_hack(self, address: Union[str, Tuple[str, int]]) -> None: + """ + Used to create a socket in advance of accept(). + + This is a workaround to ensure that we can guarantee timing of + precisely when a socket exists to avoid a connection attempt + bouncing off of nothing. + + Python 3.7+ adds a feature to separate the server creation and + listening phases instead, and should be used instead of this + hack. + """ + if isinstance(address, tuple): + family = socket.AF_INET + else: + family = socket.AF_UNIX + + sock = socket.socket(family, socket.SOCK_STREAM) + sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + + try: + sock.bind(address) + except: + sock.close() + raise + + self._sock = sock + @upper_half async def _do_accept(self, address: SocketAddrT, ssl: Optional[SSLContext] = None) -> None: @@ -464,24 +496,27 @@ class AsyncProtocol(Generic[T]): if isinstance(address, tuple): coro = asyncio.start_server( _client_connected_cb, - host=address[0], - port=address[1], + host=None if self._sock else address[0], + port=None if self._sock else address[1], ssl=ssl, backlog=1, limit=self._limit, + sock=self._sock, ) else: coro = asyncio.start_unix_server( _client_connected_cb, - path=address, + path=None if self._sock else address, ssl=ssl, backlog=1, limit=self._limit, + sock=self._sock, ) server = await coro # Starts listening await connected.wait() # Waits for the callback to fire (and finish) assert server is None + self._sock = None self.logger.debug("Connection accepted.") From 2a728de1fff4b9ceede039d078e5e33ff71908f1 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Tue, 25 Jan 2022 14:17:33 -0500 Subject: [PATCH 232/460] cpuid: use unsigned for max cpuid MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit __get_cpuid_max returns an unsigned value. For consistency, store the result in an unsigned variable. Cc: Paolo Bonzini Cc: Richard Henderson Signed-off-by: Michael S. Tsirkin Reviewed-by: Philippe Mathieu-Daudé --- configure | 2 +- tcg/i386/tcg-target.c.inc | 2 +- util/bufferiszero.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/configure b/configure index e6cfc0e4be..dfb9019b24 100755 --- a/configure +++ b/configure @@ -2768,7 +2768,7 @@ cat > $TMPC << EOF #include int main(void) { unsigned a, b, c, d; - int max = __get_cpuid_max(0, 0); + unsigned max = __get_cpuid_max(0, 0); if (max >= 1) { __cpuid(1, a, b, c, d); diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index 875311f795..4dab09f265 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -3747,7 +3747,7 @@ static void tcg_target_init(TCGContext *s) { #ifdef CONFIG_CPUID_H unsigned a, b, c, d, b7 = 0; - int max = __get_cpuid_max(0, 0); + unsigned max = __get_cpuid_max(0, 0); if (max >= 7) { /* BMI1 is available on AMD Piledriver and Intel Haswell CPUs. */ diff --git a/util/bufferiszero.c b/util/bufferiszero.c index 695bb4ce28..ec3cd4ca15 100644 --- a/util/bufferiszero.c +++ b/util/bufferiszero.c @@ -272,7 +272,7 @@ static void init_accel(unsigned cache) static void __attribute__((constructor)) init_cpuid_cache(void) { - int max = __get_cpuid_max(0, NULL); + unsigned max = __get_cpuid_max(0, NULL); int a, b, c, d; unsigned cache = 0; From 274f5e63430b907b4bf50d40458204729fcded55 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 7 Jan 2022 17:07:13 +0100 Subject: [PATCH 233/460] hw/i386: Add the possibility to disable the 'isapc' machine MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We already have a CONFIG_ISAPC switch - but we're not using it yet. Add some "#ifdefs" to make it possible to disable this machine now. Signed-off-by: Thomas Huth Message-Id: <20220107160713.235918-1-thuth@redhat.com> Acked-by: Paolo Bonzini Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/pc_piix.c | 5 ++++- tests/qtest/cdrom-test.c | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 7c7790a5ce..d9b344248d 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -357,10 +357,12 @@ static void pc_compat_1_4_fn(MachineState *machine) pc_compat_1_5_fn(machine); } +#ifdef CONFIG_ISAPC static void pc_init_isa(MachineState *machine) { pc_init1(machine, TYPE_I440FX_PCI_HOST_BRIDGE, TYPE_I440FX_PCI_DEVICE); } +#endif #ifdef CONFIG_XEN static void pc_xen_hvm_init_pci(MachineState *machine) @@ -916,6 +918,7 @@ void igd_passthrough_isa_bridge_create(PCIBus *bus, uint16_t gpu_dev_id) pci_config_set_revision(bridge_dev->config, pch_rev_id); } +#ifdef CONFIG_ISAPC static void isapc_machine_options(MachineClass *m) { PCMachineClass *pcmc = PC_MACHINE_CLASS(m); @@ -935,7 +938,7 @@ static void isapc_machine_options(MachineClass *m) DEFINE_PC_MACHINE(isapc, "isapc", pc_init_isa, isapc_machine_options); - +#endif #ifdef CONFIG_XEN static void xenfv_4_2_machine_options(MachineClass *m) diff --git a/tests/qtest/cdrom-test.c b/tests/qtest/cdrom-test.c index cfca24fa94..fdd889a487 100644 --- a/tests/qtest/cdrom-test.c +++ b/tests/qtest/cdrom-test.c @@ -138,7 +138,7 @@ static void add_x86_tests(void) * Unstable CI test under load * See https://lists.gnu.org/archive/html/qemu-devel/2019-02/msg05509.html */ - if (g_test_slow()) { + if (g_test_slow() && qtest_has_machine("isapc")) { qtest_add_data_func("cdrom/boot/isapc", "-M isapc " "-drive if=ide,media=cdrom,file=", test_cdboot); } From a849522f726767022203ef2b6c395ea19facb866 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Wed, 12 Jan 2022 08:03:29 -0500 Subject: [PATCH 234/460] tests: acpi: manually pad OEM_ID/OEM_TABLE_ID for test_oem_fields() test The next commit will revert OEM fields padding with whitespace to padding with '\0' as it was before [1]. As result test_oem_fields() will fail due to unexpectedly smaller ID sizes read from QEMU ACPI tables. Pad OEM_ID/OEM_TABLE_ID manually with spaces so that values the test puts on QEMU CLI and expected values match. 1) 602b458201 ("acpi: Permit OEM ID and OEM table ID fields to be changed") Signed-off-by: Igor Mammedov Message-Id: <20220112130332.1648664-2-imammedo@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/qtest/bios-tables-test.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/tests/qtest/bios-tables-test.c b/tests/qtest/bios-tables-test.c index e6b72d9026..90c9f6a0a2 100644 --- a/tests/qtest/bios-tables-test.c +++ b/tests/qtest/bios-tables-test.c @@ -71,9 +71,10 @@ #define ACPI_REBUILD_EXPECTED_AML "TEST_ACPI_REBUILD_AML" -#define OEM_ID "TEST" -#define OEM_TABLE_ID "OEM" -#define OEM_TEST_ARGS "-machine x-oem-id="OEM_ID",x-oem-table-id="OEM_TABLE_ID +#define OEM_ID "TEST " +#define OEM_TABLE_ID "OEM " +#define OEM_TEST_ARGS "-machine x-oem-id='" OEM_ID "',x-oem-table-id='" \ + OEM_TABLE_ID "'" typedef struct { bool tcg_only; @@ -1519,11 +1520,7 @@ static void test_acpi_q35_slic(void) static void test_oem_fields(test_data *data) { int i; - char oem_id[6]; - char oem_table_id[8]; - strpadcpy(oem_id, sizeof oem_id, OEM_ID, ' '); - strpadcpy(oem_table_id, sizeof oem_table_id, OEM_TABLE_ID, ' '); for (i = 0; i < data->tables->len; ++i) { AcpiSdtTable *sdt; @@ -1533,8 +1530,8 @@ static void test_oem_fields(test_data *data) continue; } - g_assert(memcmp(sdt->aml + 10, oem_id, 6) == 0); - g_assert(memcmp(sdt->aml + 16, oem_table_id, 8) == 0); + g_assert(memcmp(sdt->aml + 10, OEM_ID, 6) == 0); + g_assert(memcmp(sdt->aml + 16, OEM_TABLE_ID, 8) == 0); } } From d1e4a4654154925eddf0fc449fa9c92b806b9c8c Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Wed, 12 Jan 2022 08:03:30 -0500 Subject: [PATCH 235/460] tests: acpi: whitelist nvdimm's SSDT and FACP.slic expected blobs The next commit will revert OEM fields whitespace padding to padding with '\0' as it was before [1]. That will change OEM Table ID for: * SSDT.*: where it was padded from 6 characters to 8 * FACP.slic: where it was padded from 2 characters to 8 after reverting whitespace padding, it will be replaced with '\0' which effectively will shorten OEM table ID to 6 and 2 characters. Whitelist affected tables before introducing the change. 1) 602b458201 ("acpi: Permit OEM ID and OEM table ID fields to be changed") Signed-off-by: Igor Mammedov Message-Id: <20220112130332.1648664-3-imammedo@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/qtest/bios-tables-test-allowed-diff.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h index dfb8523c8b..7faa8f53be 100644 --- a/tests/qtest/bios-tables-test-allowed-diff.h +++ b/tests/qtest/bios-tables-test-allowed-diff.h @@ -1 +1,5 @@ /* List of comma-separated changed AML files to ignore */ +"tests/data/acpi/virt/SSDT.memhp", +"tests/data/acpi/pc/SSDT.dimmpxm", +"tests/data/acpi/q35/SSDT.dimmpxm", +"tests/data/acpi/q35/FACP.slic", From 748c030f360a940fe0c9382c8ca1649096c3a80d Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Wed, 12 Jan 2022 08:03:31 -0500 Subject: [PATCH 236/460] acpi: fix OEM ID/OEM Table ID padding Commit [2] broke original '\0' padding of OEM ID and OEM Table ID fields in headers of ACPI tables. While it doesn't have impact on default values since QEMU uses 6 and 8 characters long values respectively, it broke usecase where IDs are provided on QEMU CLI. It shouldn't affect guest (but may cause licensing verification issues in guest OS). One of the broken usecases is user supplied SLIC table with IDs shorter than max possible length, where [2] mangles IDs with extra spaces in RSDT and FADT tables whereas guest OS expects those to mirror the respective values of the used SLIC table. Fix it by replacing whitespace padding with '\0' padding in accordance with [1] and expectations of guest OS 1) ACPI spec, v2.0b 17.2 AML Grammar Definition ... //OEM ID of up to 6 characters. If the OEM ID is //shorter than 6 characters, it can be terminated //with a NULL character. 2) Fixes: 602b458201 ("acpi: Permit OEM ID and OEM table ID fields to be changed") Resolves: https://gitlab.com/qemu-project/qemu/-/issues/707 Reported-by: Dmitry V. Orekhov Signed-off-by: Igor Mammedov Cc: qemu-stable@nongnu.org Message-Id: <20220112130332.1648664-4-imammedo@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Reviewed-by: Ani Sinha Tested-by: Dmitry V. Orekhov dima.orekhov@gmail.com --- hw/acpi/aml-build.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/acpi/aml-build.c b/hw/acpi/aml-build.c index bb2cad63b5..8966e16320 100644 --- a/hw/acpi/aml-build.c +++ b/hw/acpi/aml-build.c @@ -1724,9 +1724,9 @@ void acpi_table_begin(AcpiTable *desc, GArray *array) build_append_int_noprefix(array, 0, 4); /* Length */ build_append_int_noprefix(array, desc->rev, 1); /* Revision */ build_append_int_noprefix(array, 0, 1); /* Checksum */ - build_append_padded_str(array, desc->oem_id, 6, ' '); /* OEMID */ + build_append_padded_str(array, desc->oem_id, 6, '\0'); /* OEMID */ /* OEM Table ID */ - build_append_padded_str(array, desc->oem_table_id, 8, ' '); + build_append_padded_str(array, desc->oem_table_id, 8, '\0'); build_append_int_noprefix(array, 1, 4); /* OEM Revision */ g_array_append_vals(array, ACPI_BUILD_APPNAME8, 4); /* Creator ID */ build_append_int_noprefix(array, 1, 4); /* Creator Revision */ From 5adc3aba875416b0e077d8a29ddd0357883746f4 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Wed, 12 Jan 2022 08:03:32 -0500 Subject: [PATCH 237/460] tests: acpi: update expected blobs Expected changes caused by previous commit: nvdimm ssdt (q35/pc/virt): - * OEM Table ID "NVDIMM " + * OEM Table ID "NVDIMM" SLIC test FADT (tests/data/acpi/q35/FACP.slic): -[010h 0016 8] Oem Table ID : "ME " +[010h 0016 8] Oem Table ID : "ME" Signed-off-by: Igor Mammedov Message-Id: <20220112130332.1648664-5-imammedo@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/data/acpi/pc/SSDT.dimmpxm | Bin 734 -> 734 bytes tests/data/acpi/q35/FACP.slic | Bin 244 -> 244 bytes tests/data/acpi/q35/SSDT.dimmpxm | Bin 734 -> 734 bytes tests/data/acpi/virt/SSDT.memhp | Bin 736 -> 736 bytes tests/qtest/bios-tables-test-allowed-diff.h | 4 ---- 5 files changed, 4 deletions(-) diff --git a/tests/data/acpi/pc/SSDT.dimmpxm b/tests/data/acpi/pc/SSDT.dimmpxm index a50a961fa1d9b0dd8ea4096d652c83bcf04db20b..ac55387d57e48adb99eb738a102308688a262fb8 100644 GIT binary patch delta 33 ocmcb|dXH5iIM^lR9uortW0;e_vq!LkUzm%huP+0`Mu}rg0HzrUKL7v# delta 33 ocmcb|dXH5iIM^lR9uortqnMMwvq!LkUzm%hudjl_Mu}rg0HV1GKL7v# diff --git a/tests/data/acpi/q35/FACP.slic b/tests/data/acpi/q35/FACP.slic index 891fd4b784b7b6b3ea303976db7ecd5b669bc84b..15986e095cf2db7ee92f7ce113c1d46d54018c62 100644 GIT binary patch delta 32 lcmeyu_=Qoz&CxmF3j+fK^CjmX$6yZyUsoUp2qsG00RW!Z2#x>% delta 32 kcmeyu_=Qoz&CxmF3j+fKvygL;W3Y#Uud4zWOq93-0G2oijsO4v diff --git a/tests/data/acpi/q35/SSDT.dimmpxm b/tests/data/acpi/q35/SSDT.dimmpxm index 617a1c911c7d6753bcedc8ecc52e3027a5259ad6..98e6f0e3f3bb02dd419e36bdd1db9b94c728c406 100644 GIT binary patch delta 33 ocmcb|dXH5iIM^lR9uortqnnezvq!LkUzm%huP+0`Mu}rg0Ho;&F8}}l delta 33 ocmcb|dXH5iIM^lR9uortBb$@Ivq!LkUzm%hudjl_Mu}rg0HKKqF8}}l diff --git a/tests/data/acpi/virt/SSDT.memhp b/tests/data/acpi/virt/SSDT.memhp index e8b850ae2239d8f496b12de672c2a1268e2f269d..375d7b6fc85a484f492a26ccd355c205f2c34473 100644 GIT binary patch delta 33 ocmaFB`hZm;IM^lR0TTlQqrH>Avq!LkUzm%huP+0`Mu`(l0HqiSFaQ7m delta 33 ocmaFB`hZm;IM^lR0TTlQ<9{cAXOCb7zc3e1Uta} Date: Fri, 14 Jan 2022 09:26:41 -0500 Subject: [PATCH 238/460] tests: acpi: test short OEM_ID/OEM_TABLE_ID values in test_oem_fields() Previous patch [1] added explicit whitespace padding to OEM_ID/OEM_TABLE_ID values used in test_oem_fields() testcase to avoid false positive and bisection issues when QEMU is switched to \0' padding. As result testcase ceased to test values that were shorter than max possible length values. Update testcase to make sure that it's testing shorter IDs like it used to before [2]. 1) "tests: acpi: manually pad OEM_ID/OEM_TABLE_ID for test_oem_fields() test" 2) 602b458201 ("acpi: Permit OEM ID and OEM table ID fields to be changed") Signed-off-by: Igor Mammedov Message-Id: <20220114142641.1727679-1-imammedo@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/qtest/bios-tables-test.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/qtest/bios-tables-test.c b/tests/qtest/bios-tables-test.c index 90c9f6a0a2..ad536fd7b1 100644 --- a/tests/qtest/bios-tables-test.c +++ b/tests/qtest/bios-tables-test.c @@ -71,10 +71,10 @@ #define ACPI_REBUILD_EXPECTED_AML "TEST_ACPI_REBUILD_AML" -#define OEM_ID "TEST " -#define OEM_TABLE_ID "OEM " -#define OEM_TEST_ARGS "-machine x-oem-id='" OEM_ID "',x-oem-table-id='" \ - OEM_TABLE_ID "'" +#define OEM_ID "TEST" +#define OEM_TABLE_ID "OEM" +#define OEM_TEST_ARGS "-machine x-oem-id=" OEM_ID ",x-oem-table-id=" \ + OEM_TABLE_ID typedef struct { bool tcg_only; @@ -1530,8 +1530,8 @@ static void test_oem_fields(test_data *data) continue; } - g_assert(memcmp(sdt->aml + 10, OEM_ID, 6) == 0); - g_assert(memcmp(sdt->aml + 16, OEM_TABLE_ID, 8) == 0); + g_assert(strncmp((char *)sdt->aml + 10, OEM_ID, 6) == 0); + g_assert(strncmp((char *)sdt->aml + 16, OEM_TABLE_ID, 8) == 0); } } From 316ee11144e3b8e1bc97a1d0fc6b1caf1963e104 Mon Sep 17 00:00:00 2001 From: Raphael Norwitz Date: Mon, 17 Jan 2022 04:12:24 +0000 Subject: [PATCH 239/460] libvhost-user: Add vu_rem_mem_reg input validation Today if multiple FDs are sent from the VMM to the backend in a VHOST_USER_REM_MEM_REG message, one FD will be unmapped and the remaining FDs will be leaked. Therefore if multiple FDs are sent we report an error and fail the operation, closing all FDs in the message. Likewise in case the VMM sends a message with a size less than that of a memory region descriptor, we add a check to gracefully report an error and fail the operation rather than crashing. Signed-off-by: Raphael Norwitz Message-Id: <20220117041050.19718-2-raphael.norwitz@nutanix.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Reviewed-by: David Hildenbrand --- subprojects/libvhost-user/libvhost-user.c | 15 +++++++++++++++ subprojects/libvhost-user/libvhost-user.h | 2 ++ 2 files changed, 17 insertions(+) diff --git a/subprojects/libvhost-user/libvhost-user.c b/subprojects/libvhost-user/libvhost-user.c index 787f4d2d4f..b09b1c269e 100644 --- a/subprojects/libvhost-user/libvhost-user.c +++ b/subprojects/libvhost-user/libvhost-user.c @@ -801,6 +801,21 @@ vu_rem_mem_reg(VuDev *dev, VhostUserMsg *vmsg) { VuDevRegion shadow_regions[VHOST_USER_MAX_RAM_SLOTS] = {}; VhostUserMemoryRegion m = vmsg->payload.memreg.region, *msg_region = &m; + if (vmsg->fd_num != 1) { + vmsg_close_fds(vmsg); + vu_panic(dev, "VHOST_USER_REM_MEM_REG received %d fds - only 1 fd " + "should be sent for this message type", vmsg->fd_num); + return false; + } + + if (vmsg->size < VHOST_USER_MEM_REG_SIZE) { + close(vmsg->fds[0]); + vu_panic(dev, "VHOST_USER_REM_MEM_REG requires a message size of at " + "least %d bytes and only %d bytes were received", + VHOST_USER_MEM_REG_SIZE, vmsg->size); + return false; + } + DPRINT("Removing region:\n"); DPRINT(" guest_phys_addr: 0x%016"PRIx64"\n", msg_region->guest_phys_addr); diff --git a/subprojects/libvhost-user/libvhost-user.h b/subprojects/libvhost-user/libvhost-user.h index 3d13dfadde..cde9f07bb3 100644 --- a/subprojects/libvhost-user/libvhost-user.h +++ b/subprojects/libvhost-user/libvhost-user.h @@ -129,6 +129,8 @@ typedef struct VhostUserMemoryRegion { uint64_t mmap_offset; } VhostUserMemoryRegion; +#define VHOST_USER_MEM_REG_SIZE (sizeof(VhostUserMemoryRegion)) + typedef struct VhostUserMemory { uint32_t nregions; uint32_t padding; From 9f4e63491ba7ae2f7a537bb98a337f4dcd4e1c54 Mon Sep 17 00:00:00 2001 From: Raphael Norwitz Date: Mon, 17 Jan 2022 04:12:31 +0000 Subject: [PATCH 240/460] libvhost-user: Add vu_add_mem_reg input validation Today if multiple FDs are sent from the VMM to the backend in a VHOST_USER_ADD_MEM_REG message, one FD will be mapped and the remaining FDs will be leaked. Therefore if multiple FDs are sent we report an error and fail the operation, closing all FDs in the message. Likewise in case the VMM sends a message with a size less than that of a memory region descriptor, we add a check to gracefully report an error and fail the operation rather than crashing. Signed-off-by: Raphael Norwitz Message-Id: <20220117041050.19718-3-raphael.norwitz@nutanix.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Reviewed-by: David Hildenbrand --- subprojects/libvhost-user/libvhost-user.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/subprojects/libvhost-user/libvhost-user.c b/subprojects/libvhost-user/libvhost-user.c index b09b1c269e..1a8fc9d600 100644 --- a/subprojects/libvhost-user/libvhost-user.c +++ b/subprojects/libvhost-user/libvhost-user.c @@ -690,6 +690,21 @@ vu_add_mem_reg(VuDev *dev, VhostUserMsg *vmsg) { VuDevRegion *dev_region = &dev->regions[dev->nregions]; void *mmap_addr; + if (vmsg->fd_num != 1) { + vmsg_close_fds(vmsg); + vu_panic(dev, "VHOST_USER_ADD_MEM_REG received %d fds - only 1 fd " + "should be sent for this message type", vmsg->fd_num); + return false; + } + + if (vmsg->size < VHOST_USER_MEM_REG_SIZE) { + close(vmsg->fds[0]); + vu_panic(dev, "VHOST_USER_ADD_MEM_REG requires a message size of at " + "least %d bytes and only %d bytes were received", + VHOST_USER_MEM_REG_SIZE, vmsg->size); + return false; + } + /* * If we are in postcopy mode and we receive a u64 payload with a 0 value * we know all the postcopy client bases have been received, and we From 4fd5ca829ae28f1f009210ab1cd42f86671015ec Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Mon, 17 Jan 2022 04:12:32 +0000 Subject: [PATCH 241/460] libvhost-user: Simplify VHOST_USER_REM_MEM_REG Let's avoid having to manually copy all elements. Copy only the ones necessary to close the hole and perform the operation in-place without a second array. Reviewed-by: Stefan Hajnoczi Signed-off-by: David Hildenbrand Signed-off-by: Raphael Norwitz Message-Id: <20220117041050.19718-4-raphael.norwitz@nutanix.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- subprojects/libvhost-user/libvhost-user.c | 30 +++++++++++------------ 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/subprojects/libvhost-user/libvhost-user.c b/subprojects/libvhost-user/libvhost-user.c index 1a8fc9d600..7dd8e918b4 100644 --- a/subprojects/libvhost-user/libvhost-user.c +++ b/subprojects/libvhost-user/libvhost-user.c @@ -811,10 +811,8 @@ static inline bool reg_equal(VuDevRegion *vudev_reg, static bool vu_rem_mem_reg(VuDev *dev, VhostUserMsg *vmsg) { - int i, j; - bool found = false; - VuDevRegion shadow_regions[VHOST_USER_MAX_RAM_SLOTS] = {}; VhostUserMemoryRegion m = vmsg->payload.memreg.region, *msg_region = &m; + int i; if (vmsg->fd_num != 1) { vmsg_close_fds(vmsg); @@ -841,28 +839,28 @@ vu_rem_mem_reg(VuDev *dev, VhostUserMsg *vmsg) { DPRINT(" mmap_offset 0x%016"PRIx64"\n", msg_region->mmap_offset); - for (i = 0, j = 0; i < dev->nregions; i++) { - if (!reg_equal(&dev->regions[i], msg_region)) { - shadow_regions[j].gpa = dev->regions[i].gpa; - shadow_regions[j].size = dev->regions[i].size; - shadow_regions[j].qva = dev->regions[i].qva; - shadow_regions[j].mmap_addr = dev->regions[i].mmap_addr; - shadow_regions[j].mmap_offset = dev->regions[i].mmap_offset; - j++; - } else { - found = true; + for (i = 0; i < dev->nregions; i++) { + if (reg_equal(&dev->regions[i], msg_region)) { VuDevRegion *r = &dev->regions[i]; void *m = (void *) (uintptr_t) r->mmap_addr; if (m) { munmap(m, r->size + r->mmap_offset); } + + break; } } - if (found) { - memcpy(dev->regions, shadow_regions, - sizeof(VuDevRegion) * VHOST_USER_MAX_RAM_SLOTS); + if (i < dev->nregions) { + /* + * Shift all affected entries by 1 to close the hole at index i and + * zero out the last entry. + */ + memmove(dev->regions + i, dev->regions + i + 1, + sizeof(VuDevRegion) * (dev->nregions - i - 1)); + memset(dev->regions + dev->nregions - 1, 0, + sizeof(VuDevRegion)); DPRINT("Successfully removed a region\n"); dev->nregions--; vmsg_set_reply_u64(vmsg, 0); From fa3d5483f0f4d4583b69ea7211c721418fca6704 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Mon, 17 Jan 2022 04:12:33 +0000 Subject: [PATCH 242/460] libvhost-user: fix VHOST_USER_REM_MEM_REG not closing the fd MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We end up not closing the file descriptor, resulting in leaking one file descriptor for each VHOST_USER_REM_MEM_REG message. Fixes: 875b9fd97b34 ("Support individual region unmap in libvhost-user") Cc: Michael S. Tsirkin Cc: Raphael Norwitz Cc: "Marc-André Lureau" Cc: Stefan Hajnoczi Cc: Paolo Bonzini Cc: Coiby Xu Signed-off-by: David Hildenbrand Signed-off-by: Raphael Norwitz Message-Id: <20220117041050.19718-5-raphael.norwitz@nutanix.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- subprojects/libvhost-user/libvhost-user.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/subprojects/libvhost-user/libvhost-user.c b/subprojects/libvhost-user/libvhost-user.c index 7dd8e918b4..3f4d7221ca 100644 --- a/subprojects/libvhost-user/libvhost-user.c +++ b/subprojects/libvhost-user/libvhost-user.c @@ -868,6 +868,8 @@ vu_rem_mem_reg(VuDev *dev, VhostUserMsg *vmsg) { vu_panic(dev, "Specified region not found\n"); } + close(vmsg->fds[0]); + return true; } From b906a23c33673f790a67dbd631a244387be77cb5 Mon Sep 17 00:00:00 2001 From: Raphael Norwitz Date: Mon, 17 Jan 2022 04:12:34 +0000 Subject: [PATCH 243/460] libvhost-user: prevent over-running max RAM slots MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When VHOST_USER_PROTOCOL_F_CONFIGURE_MEM_SLOTS support was added to libvhost-user, no guardrails were added to protect against QEMU attempting to hot-add too many RAM slots to a VM with a libvhost-user based backed attached. This change adds the missing error handling by introducing a check on the number of RAM slots the device has available before proceeding to process the VHOST_USER_ADD_MEM_REG message. Suggested-by: Stefan Hajnoczi Signed-off-by: Raphael Norwitz Message-Id: <20220117041050.19718-6-raphael.norwitz@nutanix.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Reviewed-by: David Hildenbrand Reviewed-by: Philippe Mathieu-Daudé --- subprojects/libvhost-user/libvhost-user.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/subprojects/libvhost-user/libvhost-user.c b/subprojects/libvhost-user/libvhost-user.c index 3f4d7221ca..2a1fa00a44 100644 --- a/subprojects/libvhost-user/libvhost-user.c +++ b/subprojects/libvhost-user/libvhost-user.c @@ -705,6 +705,14 @@ vu_add_mem_reg(VuDev *dev, VhostUserMsg *vmsg) { return false; } + if (dev->nregions == VHOST_USER_MAX_RAM_SLOTS) { + close(vmsg->fds[0]); + vu_panic(dev, "failing attempt to hot add memory via " + "VHOST_USER_ADD_MEM_REG message because the backend has " + "no free ram slots available"); + return false; + } + /* * If we are in postcopy mode and we receive a u64 payload with a 0 value * we know all the postcopy client bases have been received, and we From 4fafedc9daaf7d7d5ad36d8925bdddd6da5ed8c2 Mon Sep 17 00:00:00 2001 From: Raphael Norwitz Date: Mon, 17 Jan 2022 04:12:35 +0000 Subject: [PATCH 244/460] libvhost-user: handle removal of identical regions Today if QEMU (or any other VMM) has sent multiple copies of the same region to a libvhost-user based backend and then attempts to remove the region, only one instance of the region will be removed, leaving stale copies of the region in dev->regions[]. This change resolves this by having vu_rem_mem_reg() iterate through all regions in dev->regions[] and delete all matching regions. Suggested-by: Stefan Hajnoczi Signed-off-by: Raphael Norwitz Message-Id: <20220117041050.19718-7-raphael.norwitz@nutanix.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Reviewed-by: David Hildenbrand --- subprojects/libvhost-user/libvhost-user.c | 28 +++++++++++++---------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/subprojects/libvhost-user/libvhost-user.c b/subprojects/libvhost-user/libvhost-user.c index 2a1fa00a44..0ee43b8e93 100644 --- a/subprojects/libvhost-user/libvhost-user.c +++ b/subprojects/libvhost-user/libvhost-user.c @@ -821,6 +821,7 @@ static bool vu_rem_mem_reg(VuDev *dev, VhostUserMsg *vmsg) { VhostUserMemoryRegion m = vmsg->payload.memreg.region, *msg_region = &m; int i; + bool found = false; if (vmsg->fd_num != 1) { vmsg_close_fds(vmsg); @@ -856,21 +857,24 @@ vu_rem_mem_reg(VuDev *dev, VhostUserMsg *vmsg) { munmap(m, r->size + r->mmap_offset); } - break; + /* + * Shift all affected entries by 1 to close the hole at index i and + * zero out the last entry. + */ + memmove(dev->regions + i, dev->regions + i + 1, + sizeof(VuDevRegion) * (dev->nregions - i - 1)); + memset(dev->regions + dev->nregions - 1, 0, sizeof(VuDevRegion)); + DPRINT("Successfully removed a region\n"); + dev->nregions--; + i--; + + found = true; + + /* Continue the search for eventual duplicates. */ } } - if (i < dev->nregions) { - /* - * Shift all affected entries by 1 to close the hole at index i and - * zero out the last entry. - */ - memmove(dev->regions + i, dev->regions + i + 1, - sizeof(VuDevRegion) * (dev->nregions - i - 1)); - memset(dev->regions + dev->nregions - 1, 0, - sizeof(VuDevRegion)); - DPRINT("Successfully removed a region\n"); - dev->nregions--; + if (found) { vmsg_set_reply_u64(vmsg, 0); } else { vu_panic(dev, "Specified region not found\n"); From eb99baa9b371e2f2cd119ad42fa8b236830d8df1 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Tue, 11 Jan 2022 13:39:39 +0100 Subject: [PATCH 245/460] libvhost-user: Map shared RAM with MAP_NORESERVE to support virtio-mem with hugetlb MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For fd-based shared memory, MAP_NORESERVE is only effective for hugetlb, otherwise it's ignored. Older Linux versions that didn't support reservation of huge pages ignored MAP_NORESERVE completely. The first client to mmap a hugetlb fd without MAP_NORESERVE will trigger reservation of huge pages for the whole mmapped range. There are two cases to consider: 1) QEMU mapped RAM without MAP_NORESERVE We're not dealing with a sparse mapping, huge pages for the whole range have already been reserved by QEMU. An additional mmap() without MAP_NORESERVE won't have any effect on the reservation. 2) QEMU mapped RAM with MAP_NORESERVE We're delaing with a sparse mapping, no huge pages should be reserved. Further mappings without MAP_NORESERVE should be avoided. For 1), it doesn't matter if we set MAP_NORESERVE or not, so we can simply set it. For 2), we'd be overriding QEMUs decision and trigger reservation of huge pages, which might just fail if there are not sufficient huge pages around. We must map with MAP_NORESERVE. This change is required to support virtio-mem with hugetlb: a virtio-mem device mapped into the guest physical memory corresponds to a sparse memory mapping and QEMU maps this memory with MAP_NORESERVE. Whenever memory in that sparse region will be accessed by the VM, QEMU populates huge pages for the affected range by preallocating memory and handling any preallocation errors gracefully. So let's map shared RAM with MAP_NORESERVE. As libvhost-user only supports Linux, there shouldn't be anything to take care of in regard of other OS support. Without this change, libvhost-user will fail mapping the region if there are currently not enough huge pages to perform the reservation: fv_panic: libvhost-user: region mmap error: Cannot allocate memory Cc: "Marc-André Lureau" Cc: "Michael S. Tsirkin" Cc: Paolo Bonzini Cc: Raphael Norwitz Cc: Stefan Hajnoczi Cc: Dr. David Alan Gilbert Signed-off-by: David Hildenbrand Message-Id: <20220111123939.132659-1-david@redhat.com> Acked-by: Raphael Norwitz --- subprojects/libvhost-user/libvhost-user.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/subprojects/libvhost-user/libvhost-user.c b/subprojects/libvhost-user/libvhost-user.c index 0ee43b8e93..47d2efc60f 100644 --- a/subprojects/libvhost-user/libvhost-user.c +++ b/subprojects/libvhost-user/libvhost-user.c @@ -751,12 +751,12 @@ vu_add_mem_reg(VuDev *dev, VhostUserMsg *vmsg) { * accessing it before we userfault. */ mmap_addr = mmap(0, dev_region->size + dev_region->mmap_offset, - PROT_NONE, MAP_SHARED, + PROT_NONE, MAP_SHARED | MAP_NORESERVE, vmsg->fds[0], 0); } else { mmap_addr = mmap(0, dev_region->size + dev_region->mmap_offset, - PROT_READ | PROT_WRITE, MAP_SHARED, vmsg->fds[0], - 0); + PROT_READ | PROT_WRITE, MAP_SHARED | MAP_NORESERVE, + vmsg->fds[0], 0); } if (mmap_addr == MAP_FAILED) { @@ -920,7 +920,7 @@ vu_set_mem_table_exec_postcopy(VuDev *dev, VhostUserMsg *vmsg) * accessing it before we userfault */ mmap_addr = mmap(0, dev_region->size + dev_region->mmap_offset, - PROT_NONE, MAP_SHARED, + PROT_NONE, MAP_SHARED | MAP_NORESERVE, vmsg->fds[i], 0); if (mmap_addr == MAP_FAILED) { @@ -1007,7 +1007,7 @@ vu_set_mem_table_exec(VuDev *dev, VhostUserMsg *vmsg) * mapped address has to be page aligned, and we use huge * pages. */ mmap_addr = mmap(0, dev_region->size + dev_region->mmap_offset, - PROT_READ | PROT_WRITE, MAP_SHARED, + PROT_READ | PROT_WRITE, MAP_SHARED | MAP_NORESERVE, vmsg->fds[i], 0); if (mmap_addr == MAP_FAILED) { From 922f48d37ab39248b1c0f74618f536a2dbe13007 Mon Sep 17 00:00:00 2001 From: Eric DeVolder Date: Fri, 28 Jan 2022 15:38:00 -0500 Subject: [PATCH 246/460] ACPI ERST: bios-tables-test.c steps 1 and 2 Following the guidelines in tests/qtest/bios-tables-test.c, this change adds empty placeholder files per step 1 for the new ERST table, and excludes resulting changed files in bios-tables-test-allowed-diff.h per step 2. Signed-off-by: Eric DeVolder Acked-by: Igor Mammedov Message-Id: <1643402289-22216-2-git-send-email-eric.devolder@oracle.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/data/acpi/microvm/ERST.pcie | 0 tests/data/acpi/pc/DSDT.acpierst | 0 tests/data/acpi/pc/ERST.acpierst | 0 tests/data/acpi/q35/DSDT.acpierst | 0 tests/data/acpi/q35/ERST.acpierst | 0 tests/qtest/bios-tables-test-allowed-diff.h | 5 +++++ 6 files changed, 5 insertions(+) create mode 100644 tests/data/acpi/microvm/ERST.pcie create mode 100644 tests/data/acpi/pc/DSDT.acpierst create mode 100644 tests/data/acpi/pc/ERST.acpierst create mode 100644 tests/data/acpi/q35/DSDT.acpierst create mode 100644 tests/data/acpi/q35/ERST.acpierst diff --git a/tests/data/acpi/microvm/ERST.pcie b/tests/data/acpi/microvm/ERST.pcie new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/data/acpi/pc/DSDT.acpierst b/tests/data/acpi/pc/DSDT.acpierst new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/data/acpi/pc/ERST.acpierst b/tests/data/acpi/pc/ERST.acpierst new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/data/acpi/q35/DSDT.acpierst b/tests/data/acpi/q35/DSDT.acpierst new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/data/acpi/q35/ERST.acpierst b/tests/data/acpi/q35/ERST.acpierst 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..603db07711 100644 --- a/tests/qtest/bios-tables-test-allowed-diff.h +++ b/tests/qtest/bios-tables-test-allowed-diff.h @@ -1 +1,6 @@ /* List of comma-separated changed AML files to ignore */ +"tests/data/acpi/pc/DSDT.acpierst", +"tests/data/acpi/pc/ERST.acpierst", +"tests/data/acpi/q35/DSDT.acpierst", +"tests/data/acpi/q35/ERST.acpierst", +"tests/data/acpi/microvm/ERST.pcie", From 22874353ead56babe5e65286efa1dde1b2b9b7f7 Mon Sep 17 00:00:00 2001 From: Eric DeVolder Date: Fri, 28 Jan 2022 15:38:02 -0500 Subject: [PATCH 247/460] ACPI ERST: PCI device_id for ERST This change reserves the PCI device_id for the new ACPI ERST device. Signed-off-by: Eric DeVolder Acked-by: Igor Mammedov Acked-by: Ani Sinha Message-Id: <1643402289-22216-4-git-send-email-eric.devolder@oracle.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- include/hw/pci/pci.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h index 023abc0f79..c3f3c90473 100644 --- a/include/hw/pci/pci.h +++ b/include/hw/pci/pci.h @@ -108,6 +108,7 @@ extern bool pci_available; #define PCI_DEVICE_ID_REDHAT_MDPY 0x000f #define PCI_DEVICE_ID_REDHAT_NVME 0x0010 #define PCI_DEVICE_ID_REDHAT_PVPANIC 0x0011 +#define PCI_DEVICE_ID_REDHAT_ACPI_ERST 0x0012 #define PCI_DEVICE_ID_REDHAT_QXL 0x0100 #define FMT_PCIBUS PRIx64 From fb1c8f896614b5f9bbd38a07e88c134e34cfa8db Mon Sep 17 00:00:00 2001 From: Eric DeVolder Date: Fri, 28 Jan 2022 15:38:03 -0500 Subject: [PATCH 248/460] ACPI ERST: header file for ERST This change introduces the public defintions for ACPI ERST. Signed-off-by: Eric DeVolder Reviewed-by: Ani Sinha Message-Id: <1643402289-22216-5-git-send-email-eric.devolder@oracle.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- include/hw/acpi/erst.h | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 include/hw/acpi/erst.h diff --git a/include/hw/acpi/erst.h b/include/hw/acpi/erst.h new file mode 100644 index 0000000000..9d637179fe --- /dev/null +++ b/include/hw/acpi/erst.h @@ -0,0 +1,19 @@ +/* + * ACPI Error Record Serialization Table, ERST, Implementation + * + * ACPI ERST introduced in ACPI 4.0, June 16, 2009. + * ACPI Platform Error Interfaces : Error Serialization + * + * Copyright (c) 2021 Oracle and/or its affiliates. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#ifndef HW_ACPI_ERST_H +#define HW_ACPI_ERST_H + +void build_erst(GArray *table_data, BIOSLinker *linker, Object *erst_dev, + const char *oem_id, const char *oem_table_id); + +#define TYPE_ACPI_ERST "acpi-erst" + +#endif From f7e26ffa590ff26b4c6a2c513ad9ba1e6156f5b8 Mon Sep 17 00:00:00 2001 From: Eric DeVolder Date: Fri, 28 Jan 2022 15:38:04 -0500 Subject: [PATCH 249/460] ACPI ERST: support for ACPI ERST feature This implements a PCI device for ACPI ERST. This implements the non-NVRAM "mode" of operation for ERST as it is supported by Linux and Windows. Signed-off-by: Eric DeVolder Reviewed-by: Ani Sinha Message-Id: <1643402289-22216-6-git-send-email-eric.devolder@oracle.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/acpi/Kconfig | 6 + hw/acpi/erst.c | 840 +++++++++++++++++++++++++++++++++++++++++++ hw/acpi/meson.build | 1 + hw/acpi/trace-events | 15 + 4 files changed, 862 insertions(+) create mode 100644 hw/acpi/erst.c diff --git a/hw/acpi/Kconfig b/hw/acpi/Kconfig index 622b0b50b7..19caebde6c 100644 --- a/hw/acpi/Kconfig +++ b/hw/acpi/Kconfig @@ -10,6 +10,7 @@ config ACPI_X86 select ACPI_HMAT select ACPI_PIIX4 select ACPI_PCIHP + select ACPI_ERST config ACPI_X86_ICH bool @@ -60,3 +61,8 @@ config ACPI_HW_REDUCED select ACPI select ACPI_MEMORY_HOTPLUG select ACPI_NVDIMM + +config ACPI_ERST + bool + default y + depends on ACPI && PCI diff --git a/hw/acpi/erst.c b/hw/acpi/erst.c new file mode 100644 index 0000000000..8a691cb5fe --- /dev/null +++ b/hw/acpi/erst.c @@ -0,0 +1,840 @@ +/* + * ACPI Error Record Serialization Table, ERST, Implementation + * + * ACPI ERST introduced in ACPI 4.0, June 16, 2009. + * ACPI Platform Error Interfaces : Error Serialization + * + * Copyright (c) 2021 Oracle and/or its affiliates. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/qdev-core.h" +#include "exec/memory.h" +#include "qom/object.h" +#include "hw/pci/pci.h" +#include "qom/object_interfaces.h" +#include "qemu/error-report.h" +#include "migration/vmstate.h" +#include "hw/qdev-properties.h" +#include "hw/acpi/acpi.h" +#include "hw/acpi/acpi-defs.h" +#include "hw/acpi/aml-build.h" +#include "hw/acpi/bios-linker-loader.h" +#include "exec/address-spaces.h" +#include "sysemu/hostmem.h" +#include "hw/acpi/erst.h" +#include "trace.h" + +/* ACPI 4.0: Table 17-16 Serialization Actions */ +#define ACTION_BEGIN_WRITE_OPERATION 0x0 +#define ACTION_BEGIN_READ_OPERATION 0x1 +#define ACTION_BEGIN_CLEAR_OPERATION 0x2 +#define ACTION_END_OPERATION 0x3 +#define ACTION_SET_RECORD_OFFSET 0x4 +#define ACTION_EXECUTE_OPERATION 0x5 +#define ACTION_CHECK_BUSY_STATUS 0x6 +#define ACTION_GET_COMMAND_STATUS 0x7 +#define ACTION_GET_RECORD_IDENTIFIER 0x8 +#define ACTION_SET_RECORD_IDENTIFIER 0x9 +#define ACTION_GET_RECORD_COUNT 0xA +#define ACTION_BEGIN_DUMMY_WRITE_OPERATION 0xB +#define ACTION_RESERVED 0xC +#define ACTION_GET_ERROR_LOG_ADDRESS_RANGE 0xD +#define ACTION_GET_ERROR_LOG_ADDRESS_LENGTH 0xE +#define ACTION_GET_ERROR_LOG_ADDRESS_RANGE_ATTRIBUTES 0xF +#define ACTION_GET_EXECUTE_OPERATION_TIMINGS 0x10 /* ACPI 6.3 */ + +/* ACPI 4.0: Table 17-17 Command Status Definitions */ +#define STATUS_SUCCESS 0x00 +#define STATUS_NOT_ENOUGH_SPACE 0x01 +#define STATUS_HARDWARE_NOT_AVAILABLE 0x02 +#define STATUS_FAILED 0x03 +#define STATUS_RECORD_STORE_EMPTY 0x04 +#define STATUS_RECORD_NOT_FOUND 0x05 + +/* UEFI 2.1: Appendix N Common Platform Error Record */ +#define UEFI_CPER_RECORD_MIN_SIZE 128U +#define UEFI_CPER_RECORD_LENGTH_OFFSET 20U +#define UEFI_CPER_RECORD_ID_OFFSET 96U +#define IS_UEFI_CPER_RECORD(ptr) \ + (((ptr)[0] == 'C') && \ + ((ptr)[1] == 'P') && \ + ((ptr)[2] == 'E') && \ + ((ptr)[3] == 'R')) + +/* + * NOTE that when accessing CPER fields within a record, memcpy() + * is utilized to avoid a possible misaligned access on the host. + */ + +/* + * This implementation is an ACTION (cmd) and VALUE (data) + * interface consisting of just two 64-bit registers. + */ +#define ERST_REG_SIZE (16UL) +#define ERST_ACTION_OFFSET (0UL) /* action (cmd) */ +#define ERST_VALUE_OFFSET (8UL) /* argument/value (data) */ + +/* + * ERST_RECORD_SIZE is the buffer size for exchanging ERST + * record contents. Thus, it defines the maximum record size. + * As this is mapped through a PCI BAR, it must be a power of + * two and larger than UEFI_CPER_RECORD_MIN_SIZE. + * The backing storage is divided into fixed size "slots", + * each ERST_RECORD_SIZE in length, and each "slot" + * storing a single record. No attempt at optimizing storage + * through compression, compaction, etc is attempted. + * NOTE that slot 0 is reserved for the backing storage header. + * Depending upon the size of the backing storage, additional + * slots will be part of the slot 0 header in order to account + * for a record_id for each available remaining slot. + */ +/* 8KiB records, not too small, not too big */ +#define ERST_RECORD_SIZE (8192UL) + +#define ACPI_ERST_MEMDEV_PROP "memdev" +#define ACPI_ERST_RECORD_SIZE_PROP "record_size" + +/* + * From the ACPI ERST spec sections: + * A record id of all 0s is used to indicate 'unspecified' record id. + * A record id of all 1s is used to indicate empty or end. + */ +#define ERST_UNSPECIFIED_RECORD_ID (0UL) +#define ERST_EMPTY_END_RECORD_ID (~0UL) + +#define ERST_IS_VALID_RECORD_ID(rid) \ + ((rid != ERST_UNSPECIFIED_RECORD_ID) && \ + (rid != ERST_EMPTY_END_RECORD_ID)) + +/* + * Implementation-specific definitions and types. + * Values are arbitrary and chosen for this implementation. + * See erst.rst documentation for details. + */ +#define ERST_EXECUTE_OPERATION_MAGIC 0x9CUL +#define ERST_STORE_MAGIC 0x524F545354535245UL /* ERSTSTOR */ +typedef struct { + uint64_t magic; + uint32_t record_size; + uint32_t storage_offset; /* offset to record storage beyond header */ + uint16_t version; + uint16_t reserved; + uint32_t record_count; + uint64_t map[]; /* contains record_ids, and position indicates index */ +} __attribute__((packed)) ERSTStorageHeader; + +/* + * Object cast macro + */ +#define ACPIERST(obj) \ + OBJECT_CHECK(ERSTDeviceState, (obj), TYPE_ACPI_ERST) + +/* + * Main ERST device state structure + */ +typedef struct { + PCIDevice parent_obj; + + /* Backend storage */ + HostMemoryBackend *hostmem; + MemoryRegion *hostmem_mr; + uint32_t storage_size; + uint32_t default_record_size; + + /* Programming registers */ + MemoryRegion iomem_mr; + + /* Exchange buffer */ + MemoryRegion exchange_mr; + + /* Interface state */ + uint8_t operation; + uint8_t busy_status; + uint8_t command_status; + uint32_t record_offset; + uint64_t reg_action; + uint64_t reg_value; + uint64_t record_identifier; + ERSTStorageHeader *header; + unsigned first_record_index; + unsigned last_record_index; + unsigned next_record_index; + +} ERSTDeviceState; + +/*******************************************************************/ +/*******************************************************************/ +static uint8_t *get_nvram_ptr_by_index(ERSTDeviceState *s, unsigned index) +{ + uint8_t *rc = NULL; + off_t offset = (index * le32_to_cpu(s->header->record_size)); + + g_assert(offset < s->storage_size); + + rc = memory_region_get_ram_ptr(s->hostmem_mr); + rc += offset; + + return rc; +} + +static void make_erst_storage_header(ERSTDeviceState *s) +{ + ERSTStorageHeader *header = s->header; + unsigned mapsz, headersz; + + header->magic = cpu_to_le64(ERST_STORE_MAGIC); + header->record_size = cpu_to_le32(s->default_record_size); + header->version = cpu_to_le16(0x0100); + header->reserved = cpu_to_le16(0x0000); + + /* Compute mapsize */ + mapsz = s->storage_size / s->default_record_size; + mapsz *= sizeof(uint64_t); + /* Compute header+map size */ + headersz = sizeof(ERSTStorageHeader) + mapsz; + /* Round up to nearest integer multiple of ERST_RECORD_SIZE */ + headersz = QEMU_ALIGN_UP(headersz, s->default_record_size); + header->storage_offset = cpu_to_le32(headersz); + + /* + * The HostMemoryBackend initializes contents to zero, + * so all record_ids stashed in the map are zero'd. + * As well the record_count is zero. Properly initialized. + */ +} + +static void check_erst_backend_storage(ERSTDeviceState *s, Error **errp) +{ + ERSTStorageHeader *header; + uint32_t record_size; + + header = memory_region_get_ram_ptr(s->hostmem_mr); + s->header = header; + + /* Ensure pointer to header is 64-bit aligned */ + g_assert(QEMU_PTR_IS_ALIGNED(header, sizeof(uint64_t))); + + /* + * Check if header is uninitialized; HostMemoryBackend inits to 0 + */ + if (le64_to_cpu(header->magic) == 0UL) { + make_erst_storage_header(s); + } + + /* Validity check record_size */ + record_size = le32_to_cpu(header->record_size); + if (!( + (record_size) && /* non zero */ + (record_size >= UEFI_CPER_RECORD_MIN_SIZE) && + (((record_size - 1) & record_size) == 0) && /* is power of 2 */ + (record_size >= 4096) /* PAGE_SIZE */ + )) { + error_setg(errp, "ERST record_size %u is invalid", record_size); + } + + /* Validity check header */ + if (!( + (le64_to_cpu(header->magic) == ERST_STORE_MAGIC) && + ((le32_to_cpu(header->storage_offset) % record_size) == 0) && + (le16_to_cpu(header->version) == 0x0100) && + (le16_to_cpu(header->reserved) == 0) + )) { + error_setg(errp, "ERST backend storage header is invalid"); + } + + /* Check storage_size against record_size */ + if (((s->storage_size % record_size) != 0) || + (record_size > s->storage_size)) { + error_setg(errp, "ACPI ERST requires storage size be multiple of " + "record size (%uKiB)", record_size); + } + + /* Compute offset of first and last record storage slot */ + s->first_record_index = le32_to_cpu(header->storage_offset) + / record_size; + s->last_record_index = (s->storage_size / record_size); +} + +static void update_map_entry(ERSTDeviceState *s, unsigned index, + uint64_t record_id) +{ + if (index < s->last_record_index) { + s->header->map[index] = cpu_to_le64(record_id); + } +} + +static unsigned find_next_empty_record_index(ERSTDeviceState *s) +{ + unsigned rc = 0; /* 0 not a valid index */ + unsigned index = s->first_record_index; + + for (; index < s->last_record_index; ++index) { + if (le64_to_cpu(s->header->map[index]) == ERST_UNSPECIFIED_RECORD_ID) { + rc = index; + break; + } + } + + return rc; +} + +static unsigned lookup_erst_record(ERSTDeviceState *s, + uint64_t record_identifier) +{ + unsigned rc = 0; /* 0 not a valid index */ + + /* Find the record_identifier in the map */ + if (record_identifier != ERST_UNSPECIFIED_RECORD_ID) { + /* + * Count number of valid records encountered, and + * short-circuit the loop if identifier not found + */ + uint32_t record_count = le32_to_cpu(s->header->record_count); + unsigned count = 0; + unsigned index; + for (index = s->first_record_index; index < s->last_record_index && + count < record_count; ++index) { + if (le64_to_cpu(s->header->map[index]) == record_identifier) { + rc = index; + break; + } + if (le64_to_cpu(s->header->map[index]) != + ERST_UNSPECIFIED_RECORD_ID) { + ++count; + } + } + } + + return rc; +} + +/* + * ACPI 4.0: 17.4.1.1 Serialization Actions, also see + * ACPI 4.0: 17.4.2.2 Operations - Reading 6.c and 2.c + */ +static unsigned get_next_record_identifier(ERSTDeviceState *s, + uint64_t *record_identifier, bool first) +{ + unsigned found = 0; + unsigned index; + + /* For operations needing to return 'first' record identifier */ + if (first) { + /* Reset initial index to beginning */ + s->next_record_index = s->first_record_index; + } + index = s->next_record_index; + + *record_identifier = ERST_EMPTY_END_RECORD_ID; + + if (le32_to_cpu(s->header->record_count)) { + for (; index < s->last_record_index; ++index) { + if (le64_to_cpu(s->header->map[index]) != + ERST_UNSPECIFIED_RECORD_ID) { + /* where to start next time */ + s->next_record_index = index + 1; + *record_identifier = le64_to_cpu(s->header->map[index]); + found = 1; + break; + } + } + } + if (!found) { + /* at end (ie scan complete), reset */ + s->next_record_index = s->first_record_index; + } + + return STATUS_SUCCESS; +} + +/* ACPI 4.0: 17.4.2.3 Operations - Clearing */ +static unsigned clear_erst_record(ERSTDeviceState *s) +{ + unsigned rc = STATUS_RECORD_NOT_FOUND; + unsigned index; + + /* Check for valid record identifier */ + if (!ERST_IS_VALID_RECORD_ID(s->record_identifier)) { + return STATUS_FAILED; + } + + index = lookup_erst_record(s, s->record_identifier); + if (index) { + /* No need to wipe record, just invalidate its map entry */ + uint32_t record_count; + update_map_entry(s, index, ERST_UNSPECIFIED_RECORD_ID); + record_count = le32_to_cpu(s->header->record_count); + record_count -= 1; + s->header->record_count = cpu_to_le32(record_count); + rc = STATUS_SUCCESS; + } + + return rc; +} + +/* ACPI 4.0: 17.4.2.2 Operations - Reading */ +static unsigned read_erst_record(ERSTDeviceState *s) +{ + unsigned rc = STATUS_RECORD_NOT_FOUND; + unsigned exchange_length; + unsigned index; + + /* Check if backend storage is empty */ + if (le32_to_cpu(s->header->record_count) == 0) { + return STATUS_RECORD_STORE_EMPTY; + } + + exchange_length = memory_region_size(&s->exchange_mr); + + /* Check for record identifier of all 0s */ + if (s->record_identifier == ERST_UNSPECIFIED_RECORD_ID) { + /* Set to 'first' record in storage */ + get_next_record_identifier(s, &s->record_identifier, true); + /* record_identifier is now a valid id, or all 1s */ + } + + /* Check for record identifier of all 1s */ + if (s->record_identifier == ERST_EMPTY_END_RECORD_ID) { + return STATUS_FAILED; + } + + /* Validate record_offset */ + if (s->record_offset > (exchange_length - UEFI_CPER_RECORD_MIN_SIZE)) { + return STATUS_FAILED; + } + + index = lookup_erst_record(s, s->record_identifier); + if (index) { + uint8_t *nvram; + uint8_t *exchange; + uint32_t record_length; + + /* Obtain pointer to the exchange buffer */ + exchange = memory_region_get_ram_ptr(&s->exchange_mr); + exchange += s->record_offset; + /* Obtain pointer to slot in storage */ + nvram = get_nvram_ptr_by_index(s, index); + /* Validate CPER record_length */ + memcpy((uint8_t *)&record_length, + &nvram[UEFI_CPER_RECORD_LENGTH_OFFSET], + sizeof(uint32_t)); + record_length = le32_to_cpu(record_length); + if (record_length < UEFI_CPER_RECORD_MIN_SIZE) { + rc = STATUS_FAILED; + } + if ((s->record_offset + record_length) > exchange_length) { + rc = STATUS_FAILED; + } + /* If all is ok, copy the record to the exchange buffer */ + if (rc != STATUS_FAILED) { + memcpy(exchange, nvram, record_length); + rc = STATUS_SUCCESS; + } + } else { + /* + * See "Reading : 'The steps performed by the platform ...' 2.c" + * Set to 'first' record in storage + */ + get_next_record_identifier(s, &s->record_identifier, true); + } + + return rc; +} + +/* ACPI 4.0: 17.4.2.1 Operations - Writing */ +static unsigned write_erst_record(ERSTDeviceState *s) +{ + unsigned rc = STATUS_FAILED; + unsigned exchange_length; + unsigned index; + uint64_t record_identifier; + uint32_t record_length; + uint8_t *exchange; + uint8_t *nvram = NULL; + bool record_found = false; + + exchange_length = memory_region_size(&s->exchange_mr); + + /* Validate record_offset */ + if (s->record_offset > (exchange_length - UEFI_CPER_RECORD_MIN_SIZE)) { + return STATUS_FAILED; + } + + /* Obtain pointer to record in the exchange buffer */ + exchange = memory_region_get_ram_ptr(&s->exchange_mr); + exchange += s->record_offset; + + /* Validate CPER record_length */ + memcpy((uint8_t *)&record_length, &exchange[UEFI_CPER_RECORD_LENGTH_OFFSET], + sizeof(uint32_t)); + record_length = le32_to_cpu(record_length); + if (record_length < UEFI_CPER_RECORD_MIN_SIZE) { + return STATUS_FAILED; + } + if ((s->record_offset + record_length) > exchange_length) { + return STATUS_FAILED; + } + + /* Extract record identifier */ + memcpy((uint8_t *)&record_identifier, &exchange[UEFI_CPER_RECORD_ID_OFFSET], + sizeof(uint64_t)); + record_identifier = le64_to_cpu(record_identifier); + + /* Check for valid record identifier */ + if (!ERST_IS_VALID_RECORD_ID(record_identifier)) { + return STATUS_FAILED; + } + + index = lookup_erst_record(s, record_identifier); + if (index) { + /* Record found, overwrite existing record */ + nvram = get_nvram_ptr_by_index(s, index); + record_found = true; + } else { + /* Record not found, not an overwrite, allocate for write */ + index = find_next_empty_record_index(s); + if (index) { + nvram = get_nvram_ptr_by_index(s, index); + } else { + /* All slots are occupied */ + rc = STATUS_NOT_ENOUGH_SPACE; + } + } + if (nvram) { + /* Write the record into the slot */ + memcpy(nvram, exchange, record_length); + memset(nvram + record_length, exchange_length - record_length, 0xFF); + /* If a new record, increment the record_count */ + if (!record_found) { + uint32_t record_count; + record_count = le32_to_cpu(s->header->record_count); + record_count += 1; /* writing new record */ + s->header->record_count = cpu_to_le32(record_count); + } + update_map_entry(s, index, record_identifier); + rc = STATUS_SUCCESS; + } + + return rc; +} + +/*******************************************************************/ + +static uint64_t erst_rd_reg64(hwaddr addr, + uint64_t reg, unsigned size) +{ + uint64_t rdval; + uint64_t mask; + unsigned shift; + + if (size == sizeof(uint64_t)) { + /* 64b access */ + mask = 0xFFFFFFFFFFFFFFFFUL; + shift = 0; + } else { + /* 32b access */ + mask = 0x00000000FFFFFFFFUL; + shift = ((addr & 0x4) == 0x4) ? 32 : 0; + } + + rdval = reg; + rdval >>= shift; + rdval &= mask; + + return rdval; +} + +static uint64_t erst_wr_reg64(hwaddr addr, + uint64_t reg, uint64_t val, unsigned size) +{ + uint64_t wrval; + uint64_t mask; + unsigned shift; + + if (size == sizeof(uint64_t)) { + /* 64b access */ + mask = 0xFFFFFFFFFFFFFFFFUL; + shift = 0; + } else { + /* 32b access */ + mask = 0x00000000FFFFFFFFUL; + shift = ((addr & 0x4) == 0x4) ? 32 : 0; + } + + val &= mask; + val <<= shift; + mask <<= shift; + wrval = reg; + wrval &= ~mask; + wrval |= val; + + return wrval; +} + +static void erst_reg_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + ERSTDeviceState *s = (ERSTDeviceState *)opaque; + + /* + * NOTE: All actions/operations/side effects happen on the WRITE, + * by this implementation's design. The READs simply return the + * reg_value contents. + */ + trace_acpi_erst_reg_write(addr, val, size); + + switch (addr) { + case ERST_VALUE_OFFSET + 0: + case ERST_VALUE_OFFSET + 4: + s->reg_value = erst_wr_reg64(addr, s->reg_value, val, size); + break; + case ERST_ACTION_OFFSET + 0: + /* + * NOTE: all valid values written to this register are of the + * ACTION_* variety. Thus there is no need to make this a 64-bit + * register, 32-bits is appropriate. As such ERST_ACTION_OFFSET+4 + * is not needed. + */ + switch (val) { + case ACTION_BEGIN_WRITE_OPERATION: + case ACTION_BEGIN_READ_OPERATION: + case ACTION_BEGIN_CLEAR_OPERATION: + case ACTION_BEGIN_DUMMY_WRITE_OPERATION: + case ACTION_END_OPERATION: + s->operation = val; + break; + case ACTION_SET_RECORD_OFFSET: + s->record_offset = s->reg_value; + break; + case ACTION_EXECUTE_OPERATION: + if ((uint8_t)s->reg_value == ERST_EXECUTE_OPERATION_MAGIC) { + s->busy_status = 1; + switch (s->operation) { + case ACTION_BEGIN_WRITE_OPERATION: + s->command_status = write_erst_record(s); + break; + case ACTION_BEGIN_READ_OPERATION: + s->command_status = read_erst_record(s); + break; + case ACTION_BEGIN_CLEAR_OPERATION: + s->command_status = clear_erst_record(s); + break; + case ACTION_BEGIN_DUMMY_WRITE_OPERATION: + s->command_status = STATUS_SUCCESS; + break; + case ACTION_END_OPERATION: + s->command_status = STATUS_SUCCESS; + break; + default: + s->command_status = STATUS_FAILED; + break; + } + s->busy_status = 0; + } + break; + case ACTION_CHECK_BUSY_STATUS: + s->reg_value = s->busy_status; + break; + case ACTION_GET_COMMAND_STATUS: + s->reg_value = s->command_status; + break; + case ACTION_GET_RECORD_IDENTIFIER: + s->command_status = get_next_record_identifier(s, + &s->reg_value, false); + break; + case ACTION_SET_RECORD_IDENTIFIER: + s->record_identifier = s->reg_value; + break; + case ACTION_GET_RECORD_COUNT: + s->reg_value = le32_to_cpu(s->header->record_count); + break; + case ACTION_GET_ERROR_LOG_ADDRESS_RANGE: + s->reg_value = (hwaddr)pci_get_bar_addr(PCI_DEVICE(s), 1); + break; + case ACTION_GET_ERROR_LOG_ADDRESS_LENGTH: + s->reg_value = le32_to_cpu(s->header->record_size); + break; + case ACTION_GET_ERROR_LOG_ADDRESS_RANGE_ATTRIBUTES: + s->reg_value = 0x0; /* intentional, not NVRAM mode */ + break; + case ACTION_GET_EXECUTE_OPERATION_TIMINGS: + s->reg_value = + (100ULL << 32) | /* 100us max time */ + (10ULL << 0) ; /* 10us min time */ + break; + default: + /* Unknown action/command, NOP */ + break; + } + break; + default: + /* This should not happen, but if it does, NOP */ + break; + } +} + +static uint64_t erst_reg_read(void *opaque, hwaddr addr, + unsigned size) +{ + ERSTDeviceState *s = (ERSTDeviceState *)opaque; + uint64_t val = 0; + + switch (addr) { + case ERST_ACTION_OFFSET + 0: + case ERST_ACTION_OFFSET + 4: + val = erst_rd_reg64(addr, s->reg_action, size); + break; + case ERST_VALUE_OFFSET + 0: + case ERST_VALUE_OFFSET + 4: + val = erst_rd_reg64(addr, s->reg_value, size); + break; + default: + break; + } + trace_acpi_erst_reg_read(addr, val, size); + return val; +} + +static const MemoryRegionOps erst_reg_ops = { + .read = erst_reg_read, + .write = erst_reg_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +/*******************************************************************/ +/*******************************************************************/ +static int erst_post_load(void *opaque, int version_id) +{ + ERSTDeviceState *s = opaque; + + /* Recompute pointer to header */ + s->header = (ERSTStorageHeader *)get_nvram_ptr_by_index(s, 0); + trace_acpi_erst_post_load(s->header, le32_to_cpu(s->header->record_size)); + + return 0; +} + +static const VMStateDescription erst_vmstate = { + .name = "acpi-erst", + .version_id = 1, + .minimum_version_id = 1, + .post_load = erst_post_load, + .fields = (VMStateField[]) { + VMSTATE_UINT8(operation, ERSTDeviceState), + VMSTATE_UINT8(busy_status, ERSTDeviceState), + VMSTATE_UINT8(command_status, ERSTDeviceState), + VMSTATE_UINT32(record_offset, ERSTDeviceState), + VMSTATE_UINT64(reg_action, ERSTDeviceState), + VMSTATE_UINT64(reg_value, ERSTDeviceState), + VMSTATE_UINT64(record_identifier, ERSTDeviceState), + VMSTATE_UINT32(next_record_index, ERSTDeviceState), + VMSTATE_END_OF_LIST() + } +}; + +static void erst_realizefn(PCIDevice *pci_dev, Error **errp) +{ + ERSTDeviceState *s = ACPIERST(pci_dev); + + trace_acpi_erst_realizefn_in(); + + if (!s->hostmem) { + error_setg(errp, "'" ACPI_ERST_MEMDEV_PROP "' property is not set"); + return; + } else if (host_memory_backend_is_mapped(s->hostmem)) { + error_setg(errp, "can't use already busy memdev: %s", + object_get_canonical_path_component(OBJECT(s->hostmem))); + return; + } + + s->hostmem_mr = host_memory_backend_get_memory(s->hostmem); + + /* HostMemoryBackend size will be multiple of PAGE_SIZE */ + s->storage_size = object_property_get_int(OBJECT(s->hostmem), "size", errp); + + /* Initialize backend storage and record_count */ + check_erst_backend_storage(s, errp); + + /* BAR 0: Programming registers */ + memory_region_init_io(&s->iomem_mr, OBJECT(pci_dev), &erst_reg_ops, s, + TYPE_ACPI_ERST, ERST_REG_SIZE); + pci_register_bar(pci_dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->iomem_mr); + + /* BAR 1: Exchange buffer memory */ + memory_region_init_ram(&s->exchange_mr, OBJECT(pci_dev), + "erst.exchange", + le32_to_cpu(s->header->record_size), errp); + pci_register_bar(pci_dev, 1, PCI_BASE_ADDRESS_SPACE_MEMORY, + &s->exchange_mr); + + /* Include the backend storage in the migration stream */ + vmstate_register_ram_global(s->hostmem_mr); + + trace_acpi_erst_realizefn_out(s->storage_size); +} + +static void erst_reset(DeviceState *dev) +{ + ERSTDeviceState *s = ACPIERST(dev); + + trace_acpi_erst_reset_in(le32_to_cpu(s->header->record_count)); + s->operation = 0; + s->busy_status = 0; + s->command_status = STATUS_SUCCESS; + s->record_identifier = ERST_UNSPECIFIED_RECORD_ID; + s->record_offset = 0; + s->next_record_index = s->first_record_index; + /* NOTE: first/last_record_index are computed only once */ + trace_acpi_erst_reset_out(le32_to_cpu(s->header->record_count)); +} + +static Property erst_properties[] = { + DEFINE_PROP_LINK(ACPI_ERST_MEMDEV_PROP, ERSTDeviceState, hostmem, + TYPE_MEMORY_BACKEND, HostMemoryBackend *), + DEFINE_PROP_UINT32(ACPI_ERST_RECORD_SIZE_PROP, ERSTDeviceState, + default_record_size, ERST_RECORD_SIZE), + DEFINE_PROP_END_OF_LIST(), +}; + +static void erst_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + trace_acpi_erst_class_init_in(); + k->realize = erst_realizefn; + k->vendor_id = PCI_VENDOR_ID_REDHAT; + k->device_id = PCI_DEVICE_ID_REDHAT_ACPI_ERST; + k->revision = 0x00; + k->class_id = PCI_CLASS_OTHERS; + dc->reset = erst_reset; + dc->vmsd = &erst_vmstate; + dc->user_creatable = true; + dc->hotpluggable = false; + device_class_set_props(dc, erst_properties); + dc->desc = "ACPI Error Record Serialization Table (ERST) device"; + set_bit(DEVICE_CATEGORY_MISC, dc->categories); + trace_acpi_erst_class_init_out(); +} + +static const TypeInfo erst_type_info = { + .name = TYPE_ACPI_ERST, + .parent = TYPE_PCI_DEVICE, + .class_init = erst_class_init, + .instance_size = sizeof(ERSTDeviceState), + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { } + } +}; + +static void erst_register_types(void) +{ + type_register_static(&erst_type_info); +} + +type_init(erst_register_types) diff --git a/hw/acpi/meson.build b/hw/acpi/meson.build index adf6347bc4..f5b22983bb 100644 --- a/hw/acpi/meson.build +++ b/hw/acpi/meson.build @@ -22,6 +22,7 @@ acpi_ss.add(when: 'CONFIG_ACPI_PCIHP', if_true: files('pcihp.c')) acpi_ss.add(when: 'CONFIG_ACPI_PCIHP', if_false: files('acpi-pci-hotplug-stub.c')) acpi_ss.add(when: 'CONFIG_ACPI_VIOT', if_true: files('viot.c')) acpi_ss.add(when: 'CONFIG_ACPI_X86_ICH', if_true: files('ich9.c', 'tco.c')) +acpi_ss.add(when: 'CONFIG_ACPI_ERST', if_true: files('erst.c')) acpi_ss.add(when: 'CONFIG_IPMI', if_true: files('ipmi.c'), if_false: files('ipmi-stub.c')) acpi_ss.add(when: 'CONFIG_PC', if_false: files('acpi-x86-stub.c')) acpi_ss.add(when: 'CONFIG_TPM', if_true: files('tpm.c')) diff --git a/hw/acpi/trace-events b/hw/acpi/trace-events index 974d770e8b..2250126a22 100644 --- a/hw/acpi/trace-events +++ b/hw/acpi/trace-events @@ -55,3 +55,18 @@ piix4_gpe_writeb(uint64_t addr, unsigned width, uint64_t val) "addr: 0x%" PRIx64 # tco.c tco_timer_reload(int ticks, int msec) "ticks=%d (%d ms)" tco_timer_expired(int timeouts_no, bool strap, bool no_reboot) "timeouts_no=%d no_reboot=%d/%d" + +# erst.c +acpi_erst_reg_write(uint64_t addr, uint64_t val, unsigned size) "addr: 0x%04" PRIx64 " <== 0x%016" PRIx64 " (size: %u)" +acpi_erst_reg_read(uint64_t addr, uint64_t val, unsigned size) " addr: 0x%04" PRIx64 " ==> 0x%016" PRIx64 " (size: %u)" +acpi_erst_mem_write(uint64_t addr, uint64_t val, unsigned size) "addr: 0x%06" PRIx64 " <== 0x%016" PRIx64 " (size: %u)" +acpi_erst_mem_read(uint64_t addr, uint64_t val, unsigned size) " addr: 0x%06" PRIx64 " ==> 0x%016" PRIx64 " (size: %u)" +acpi_erst_pci_bar_0(uint64_t addr) "BAR0: 0x%016" PRIx64 +acpi_erst_pci_bar_1(uint64_t addr) "BAR1: 0x%016" PRIx64 +acpi_erst_realizefn_in(void) +acpi_erst_realizefn_out(unsigned size) "total nvram size %u bytes" +acpi_erst_reset_in(unsigned record_count) "record_count %u" +acpi_erst_reset_out(unsigned record_count) "record_count %u" +acpi_erst_post_load(void *header, unsigned slot_size) "header: 0x%p slot_size %u" +acpi_erst_class_init_in(void) +acpi_erst_class_init_out(void) From c9cd06ca00acd142c3249594b041d7c84409e536 Mon Sep 17 00:00:00 2001 From: Eric DeVolder Date: Fri, 28 Jan 2022 15:38:05 -0500 Subject: [PATCH 250/460] ACPI ERST: build the ACPI ERST table This builds the ACPI ERST table to inform OSPM how to communicate with the acpi-erst device. Signed-off-by: Eric DeVolder Reviewed-by: Ani Sinha Message-Id: <1643402289-22216-7-git-send-email-eric.devolder@oracle.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/acpi/erst.c | 211 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 211 insertions(+) diff --git a/hw/acpi/erst.c b/hw/acpi/erst.c index 8a691cb5fe..c0a23cf467 100644 --- a/hw/acpi/erst.c +++ b/hw/acpi/erst.c @@ -55,6 +55,27 @@ #define STATUS_RECORD_STORE_EMPTY 0x04 #define STATUS_RECORD_NOT_FOUND 0x05 +/* ACPI 4.0: Table 17-19 Serialization Instructions */ +#define INST_READ_REGISTER 0x00 +#define INST_READ_REGISTER_VALUE 0x01 +#define INST_WRITE_REGISTER 0x02 +#define INST_WRITE_REGISTER_VALUE 0x03 +#define INST_NOOP 0x04 +#define INST_LOAD_VAR1 0x05 +#define INST_LOAD_VAR2 0x06 +#define INST_STORE_VAR1 0x07 +#define INST_ADD 0x08 +#define INST_SUBTRACT 0x09 +#define INST_ADD_VALUE 0x0A +#define INST_SUBTRACT_VALUE 0x0B +#define INST_STALL 0x0C +#define INST_STALL_WHILE_TRUE 0x0D +#define INST_SKIP_NEXT_INSTRUCTION_IF_TRUE 0x0E +#define INST_GOTO 0x0F +#define INST_SET_SRC_ADDRESS_BASE 0x10 +#define INST_SET_DST_ADDRESS_BASE 0x11 +#define INST_MOVE_DATA 0x12 + /* UEFI 2.1: Appendix N Common Platform Error Record */ #define UEFI_CPER_RECORD_MIN_SIZE 128U #define UEFI_CPER_RECORD_LENGTH_OFFSET 20U @@ -166,6 +187,196 @@ typedef struct { } ERSTDeviceState; +/*******************************************************************/ +/*******************************************************************/ +typedef struct { + GArray *table_data; + pcibus_t bar; + uint8_t instruction; + uint8_t flags; + uint8_t register_bit_width; + pcibus_t register_offset; +} BuildSerializationInstructionEntry; + +/* ACPI 4.0: 17.4.1.2 Serialization Instruction Entries */ +static void build_serialization_instruction( + BuildSerializationInstructionEntry *e, + uint8_t serialization_action, + uint64_t value) +{ + /* ACPI 4.0: Table 17-18 Serialization Instruction Entry */ + struct AcpiGenericAddress gas; + uint64_t mask; + + /* Serialization Action */ + build_append_int_noprefix(e->table_data, serialization_action, 1); + /* Instruction */ + build_append_int_noprefix(e->table_data, e->instruction, 1); + /* Flags */ + build_append_int_noprefix(e->table_data, e->flags, 1); + /* Reserved */ + build_append_int_noprefix(e->table_data, 0, 1); + /* Register Region */ + gas.space_id = AML_SYSTEM_MEMORY; + gas.bit_width = e->register_bit_width; + gas.bit_offset = 0; + gas.access_width = (uint8_t)ctz32(e->register_bit_width) - 2; + gas.address = (uint64_t)(e->bar + e->register_offset); + build_append_gas_from_struct(e->table_data, &gas); + /* Value */ + build_append_int_noprefix(e->table_data, value, 8); + /* Mask */ + mask = (1ULL << (e->register_bit_width - 1) << 1) - 1; + build_append_int_noprefix(e->table_data, mask, 8); +} + +/* ACPI 4.0: 17.4.1 Serialization Action Table */ +void build_erst(GArray *table_data, BIOSLinker *linker, Object *erst_dev, + const char *oem_id, const char *oem_table_id) +{ + /* + * Serialization Action Table + * The serialization action table must be generated first + * so that its size can be known in order to populate the + * Instruction Entry Count field. + */ + unsigned action; + GArray *table_instruction_data = g_array_new(FALSE, FALSE, sizeof(char)); + pcibus_t bar0 = pci_get_bar_addr(PCI_DEVICE(erst_dev), 0); + AcpiTable table = { .sig = "ERST", .rev = 1, .oem_id = oem_id, + .oem_table_id = oem_table_id }; + /* Contexts for the different ways ACTION and VALUE are accessed */ + BuildSerializationInstructionEntry rd_value_32_val = { + .table_data = table_instruction_data, .bar = bar0, .flags = 0, + .instruction = INST_READ_REGISTER_VALUE, + .register_bit_width = 32, + .register_offset = ERST_VALUE_OFFSET, + }; + BuildSerializationInstructionEntry rd_value_32 = { + .table_data = table_instruction_data, .bar = bar0, .flags = 0, + .instruction = INST_READ_REGISTER, + .register_bit_width = 32, + .register_offset = ERST_VALUE_OFFSET, + }; + BuildSerializationInstructionEntry rd_value_64 = { + .table_data = table_instruction_data, .bar = bar0, .flags = 0, + .instruction = INST_READ_REGISTER, + .register_bit_width = 64, + .register_offset = ERST_VALUE_OFFSET, + }; + BuildSerializationInstructionEntry wr_value_32_val = { + .table_data = table_instruction_data, .bar = bar0, .flags = 0, + .instruction = INST_WRITE_REGISTER_VALUE, + .register_bit_width = 32, + .register_offset = ERST_VALUE_OFFSET, + }; + BuildSerializationInstructionEntry wr_value_32 = { + .table_data = table_instruction_data, .bar = bar0, .flags = 0, + .instruction = INST_WRITE_REGISTER, + .register_bit_width = 32, + .register_offset = ERST_VALUE_OFFSET, + }; + BuildSerializationInstructionEntry wr_value_64 = { + .table_data = table_instruction_data, .bar = bar0, .flags = 0, + .instruction = INST_WRITE_REGISTER, + .register_bit_width = 64, + .register_offset = ERST_VALUE_OFFSET, + }; + BuildSerializationInstructionEntry wr_action = { + .table_data = table_instruction_data, .bar = bar0, .flags = 0, + .instruction = INST_WRITE_REGISTER_VALUE, + .register_bit_width = 32, + .register_offset = ERST_ACTION_OFFSET, + }; + + trace_acpi_erst_pci_bar_0(bar0); + + /* Serialization Instruction Entries */ + action = ACTION_BEGIN_WRITE_OPERATION; + build_serialization_instruction(&wr_action, action, action); + + action = ACTION_BEGIN_READ_OPERATION; + build_serialization_instruction(&wr_action, action, action); + + action = ACTION_BEGIN_CLEAR_OPERATION; + build_serialization_instruction(&wr_action, action, action); + + action = ACTION_END_OPERATION; + build_serialization_instruction(&wr_action, action, action); + + action = ACTION_SET_RECORD_OFFSET; + build_serialization_instruction(&wr_value_32, action, 0); + build_serialization_instruction(&wr_action, action, action); + + action = ACTION_EXECUTE_OPERATION; + build_serialization_instruction(&wr_value_32_val, action, + ERST_EXECUTE_OPERATION_MAGIC); + build_serialization_instruction(&wr_action, action, action); + + action = ACTION_CHECK_BUSY_STATUS; + build_serialization_instruction(&wr_action, action, action); + build_serialization_instruction(&rd_value_32_val, action, 0x01); + + action = ACTION_GET_COMMAND_STATUS; + build_serialization_instruction(&wr_action, action, action); + build_serialization_instruction(&rd_value_32, action, 0); + + action = ACTION_GET_RECORD_IDENTIFIER; + build_serialization_instruction(&wr_action, action, action); + build_serialization_instruction(&rd_value_64, action, 0); + + action = ACTION_SET_RECORD_IDENTIFIER; + build_serialization_instruction(&wr_value_64, action, 0); + build_serialization_instruction(&wr_action, action, action); + + action = ACTION_GET_RECORD_COUNT; + build_serialization_instruction(&wr_action, action, action); + build_serialization_instruction(&rd_value_32, action, 0); + + action = ACTION_BEGIN_DUMMY_WRITE_OPERATION; + build_serialization_instruction(&wr_action, action, action); + + action = ACTION_GET_ERROR_LOG_ADDRESS_RANGE; + build_serialization_instruction(&wr_action, action, action); + build_serialization_instruction(&rd_value_64, action, 0); + + action = ACTION_GET_ERROR_LOG_ADDRESS_LENGTH; + build_serialization_instruction(&wr_action, action, action); + build_serialization_instruction(&rd_value_64, action, 0); + + action = ACTION_GET_ERROR_LOG_ADDRESS_RANGE_ATTRIBUTES; + build_serialization_instruction(&wr_action, action, action); + build_serialization_instruction(&rd_value_32, action, 0); + + action = ACTION_GET_EXECUTE_OPERATION_TIMINGS; + build_serialization_instruction(&wr_action, action, action); + build_serialization_instruction(&rd_value_64, action, 0); + + /* Serialization Header */ + acpi_table_begin(&table, table_data); + + /* Serialization Header Size */ + build_append_int_noprefix(table_data, 48, 4); + + /* Reserved */ + build_append_int_noprefix(table_data, 0, 4); + + /* + * Instruction Entry Count + * Each instruction entry is 32 bytes + */ + g_assert((table_instruction_data->len) % 32 == 0); + build_append_int_noprefix(table_data, + (table_instruction_data->len / 32), 4); + + /* Serialization Instruction Entries */ + g_array_append_vals(table_data, table_instruction_data->data, + table_instruction_data->len); + g_array_free(table_instruction_data, TRUE); + + acpi_table_end(linker, &table); +} + /*******************************************************************/ /*******************************************************************/ static uint8_t *get_nvram_ptr_by_index(ERSTDeviceState *s, unsigned index) From 8486f12f0b1e74217cc2769b6e2f38826b5a5444 Mon Sep 17 00:00:00 2001 From: Eric DeVolder Date: Fri, 28 Jan 2022 15:38:06 -0500 Subject: [PATCH 251/460] ACPI ERST: create ACPI ERST table for pc/x86 machines This change exposes ACPI ERST support for x86 guests. Signed-off-by: Eric DeVolder Reviewed-by: Ani Sinha Message-Id: <1643402289-22216-8-git-send-email-eric.devolder@oracle.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/acpi-build.c | 15 +++++++++++++++ hw/i386/acpi-microvm.c | 15 +++++++++++++++ include/hw/acpi/erst.h | 5 +++++ 3 files changed, 35 insertions(+) diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index ce823e8fcb..ebd47aa26f 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -43,6 +43,7 @@ #include "sysemu/tpm.h" #include "hw/acpi/tpm.h" #include "hw/acpi/vmgenid.h" +#include "hw/acpi/erst.h" #include "sysemu/tpm_backend.h" #include "hw/rtc/mc146818rtc_regs.h" #include "migration/vmstate.h" @@ -74,6 +75,8 @@ #include "hw/acpi/hmat.h" #include "hw/acpi/viot.h" +#include CONFIG_DEVICES + /* These are used to size the ACPI tables for -M pc-i440fx-1.7 and * -M pc-i440fx-2.0. Even if the actual amount of AML generated grows * a little bit, there should be plenty of free space since the DSDT @@ -2575,6 +2578,18 @@ void acpi_build(AcpiBuildTables *tables, MachineState *machine) ACPI_DEVICE_IF(x86ms->acpi_dev), x86ms->oem_id, x86ms->oem_table_id); +#ifdef CONFIG_ACPI_ERST + { + Object *erst_dev; + erst_dev = find_erst_dev(); + if (erst_dev) { + acpi_add_table(table_offsets, tables_blob); + build_erst(tables_blob, tables->linker, erst_dev, + x86ms->oem_id, x86ms->oem_table_id); + } + } +#endif + vmgenid_dev = find_vmgenid_dev(); if (vmgenid_dev) { acpi_add_table(table_offsets, tables_blob); diff --git a/hw/i386/acpi-microvm.c b/hw/i386/acpi-microvm.c index 196d318499..68ca7e7fc2 100644 --- a/hw/i386/acpi-microvm.c +++ b/hw/i386/acpi-microvm.c @@ -30,6 +30,7 @@ #include "hw/acpi/bios-linker-loader.h" #include "hw/acpi/generic_event_device.h" #include "hw/acpi/utils.h" +#include "hw/acpi/erst.h" #include "hw/i386/fw_cfg.h" #include "hw/i386/microvm.h" #include "hw/pci/pci.h" @@ -40,6 +41,8 @@ #include "acpi-common.h" #include "acpi-microvm.h" +#include CONFIG_DEVICES + static void acpi_dsdt_add_virtio(Aml *scope, MicrovmMachineState *mms) { @@ -207,6 +210,18 @@ static void acpi_build_microvm(AcpiBuildTables *tables, ACPI_DEVICE_IF(x86ms->acpi_dev), x86ms->oem_id, x86ms->oem_table_id); +#ifdef CONFIG_ACPI_ERST + { + Object *erst_dev; + erst_dev = find_erst_dev(); + if (erst_dev) { + acpi_add_table(table_offsets, tables_blob); + build_erst(tables_blob, tables->linker, erst_dev, + x86ms->oem_id, x86ms->oem_table_id); + } + } +#endif + xsdt = tables_blob->len; build_xsdt(tables_blob, tables->linker, table_offsets, x86ms->oem_id, x86ms->oem_table_id); diff --git a/include/hw/acpi/erst.h b/include/hw/acpi/erst.h index 9d637179fe..b747fe7739 100644 --- a/include/hw/acpi/erst.h +++ b/include/hw/acpi/erst.h @@ -16,4 +16,9 @@ void build_erst(GArray *table_data, BIOSLinker *linker, Object *erst_dev, #define TYPE_ACPI_ERST "acpi-erst" +/* returns NULL unless there is exactly one device */ +static inline Object *find_erst_dev(void) +{ + return object_resolve_path_type("", TYPE_ACPI_ERST, NULL); +} #endif From bd24550e5c34eb8b1c3911f22c6054f8d41d50b9 Mon Sep 17 00:00:00 2001 From: Eric DeVolder Date: Fri, 28 Jan 2022 15:38:07 -0500 Subject: [PATCH 252/460] ACPI ERST: qtest for ERST This change provides a qtest that locates and then does a simple interrogation of the ERST feature within the guest. Signed-off-by: Eric DeVolder Reviewed-by: Ani Sinha Message-Id: <1643402289-22216-9-git-send-email-eric.devolder@oracle.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/qtest/erst-test.c | 164 ++++++++++++++++++++++++++++++++++++++++ tests/qtest/meson.build | 2 + 2 files changed, 166 insertions(+) create mode 100644 tests/qtest/erst-test.c diff --git a/tests/qtest/erst-test.c b/tests/qtest/erst-test.c new file mode 100644 index 0000000000..c6a0ae4013 --- /dev/null +++ b/tests/qtest/erst-test.c @@ -0,0 +1,164 @@ +/* + * QTest testcase for acpi-erst + * + * Copyright (c) 2021 Oracle + * + * 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 "libqos/libqos-pc.h" +#include "libqos/libqtest.h" +#include "qemu-common.h" + +#include "hw/pci/pci.h" + +static void save_fn(QPCIDevice *dev, int devfn, void *data) +{ + QPCIDevice **pdev = (QPCIDevice **) data; + + *pdev = dev; +} + +static QPCIDevice *get_erst_device(QPCIBus *pcibus) +{ + QPCIDevice *dev; + + dev = NULL; + qpci_device_foreach(pcibus, + PCI_VENDOR_ID_REDHAT, + PCI_DEVICE_ID_REDHAT_ACPI_ERST, + save_fn, &dev); + g_assert(dev != NULL); + + return dev; +} + +typedef struct _ERSTState { + QOSState *qs; + QPCIBar reg_bar, mem_bar; + uint64_t reg_barsize, mem_barsize; + QPCIDevice *dev; +} ERSTState; + +#define ACTION 0 +#define VALUE 8 + +static const char *reg2str(unsigned reg) +{ + switch (reg) { + case 0: + return "ACTION"; + case 8: + return "VALUE"; + default: + return NULL; + } +} + +static inline uint32_t in_reg32(ERSTState *s, unsigned reg) +{ + const char *name = reg2str(reg); + uint32_t res; + + res = qpci_io_readl(s->dev, s->reg_bar, reg); + g_test_message("*%s -> %08x", name, res); + + return res; +} + +static inline uint64_t in_reg64(ERSTState *s, unsigned reg) +{ + const char *name = reg2str(reg); + uint64_t res; + + res = qpci_io_readq(s->dev, s->reg_bar, reg); + g_test_message("*%s -> %016llx", name, (unsigned long long)res); + + return res; +} + +static inline void out_reg32(ERSTState *s, unsigned reg, uint32_t v) +{ + const char *name = reg2str(reg); + + g_test_message("%08x -> *%s", v, name); + qpci_io_writel(s->dev, s->reg_bar, reg, v); +} + +static void cleanup_vm(ERSTState *s) +{ + g_free(s->dev); + qtest_shutdown(s->qs); +} + +static void setup_vm_cmd(ERSTState *s, const char *cmd) +{ + const char *arch = qtest_get_arch(); + + if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { + s->qs = qtest_pc_boot(cmd); + } else { + g_printerr("erst-test tests are only available on x86\n"); + exit(EXIT_FAILURE); + } + s->dev = get_erst_device(s->qs->pcibus); + + s->reg_bar = qpci_iomap(s->dev, 0, &s->reg_barsize); + 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); + + qpci_device_enable(s->dev); +} + +static void test_acpi_erst_basic(void) +{ + ERSTState state; + uint64_t log_address_range; + uint64_t log_address_length; + uint32_t log_address_attr; + + setup_vm_cmd(&state, + "-object memory-backend-file," + "mem-path=acpi-erst.XXXXXX," + "size=64K," + "share=on," + "id=nvram " + "-device acpi-erst," + "memdev=nvram"); + + out_reg32(&state, ACTION, 0xD); + log_address_range = in_reg64(&state, VALUE); + out_reg32(&state, ACTION, 0xE); + log_address_length = in_reg64(&state, VALUE); + out_reg32(&state, ACTION, 0xF); + log_address_attr = in_reg32(&state, VALUE); + + /* Check log_address_range is not 0, ~0 or base */ + g_assert_cmpuint(log_address_range, !=, 0ULL); + g_assert_cmpuint(log_address_range, !=, ~0ULL); + g_assert_cmpuint(log_address_range, !=, state.reg_bar.addr); + g_assert_cmpuint(log_address_range, ==, state.mem_bar.addr); + + /* Check log_address_length is bar1_size */ + g_assert_cmpuint(log_address_length, ==, state.mem_barsize); + + /* Check log_address_attr is 0 */ + g_assert_cmpuint(log_address_attr, ==, 0); + + cleanup_vm(&state); +} + +int main(int argc, char **argv) +{ + int ret; + + g_test_init(&argc, &argv, NULL); + qtest_add_func("/acpi-erst/basic", test_acpi_erst_basic); + ret = g_test_run(); + return ret; +} diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index 842b1df420..762f6adcd5 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -68,6 +68,7 @@ qtests_i386 = \ (config_all_devices.has_key('CONFIG_RTL8139_PCI') ? ['rtl8139-test'] : []) + \ (config_all_devices.has_key('CONFIG_E1000E_PCI_EXPRESS') ? ['fuzz-e1000e-test'] : []) + \ (config_all_devices.has_key('CONFIG_ESP_PCI') ? ['am53c974-test'] : []) + \ + (config_all_devices.has_key('CONFIG_ACPI_ERST') ? ['erst-test'] : []) + \ (config_all_devices.has_key('CONFIG_VIRTIO_NET') and \ config_all_devices.has_key('CONFIG_Q35') and \ config_all_devices.has_key('CONFIG_VIRTIO_PCI') and \ @@ -278,6 +279,7 @@ qtests = { 'bios-tables-test': [io, 'boot-sector.c', 'acpi-utils.c', 'tpm-emu.c'], 'cdrom-test': files('boot-sector.c'), 'dbus-vmstate-test': files('migration-helpers.c') + dbus_vmstate1, + 'erst-test': files('erst-test.c'), 'ivshmem-test': [rt, '../../contrib/ivshmem-server/ivshmem-server.c'], 'migration-test': files('migration-helpers.c'), 'pxe-test': files('boot-sector.c'), From 646a793cc36891aaff57d7cb57bb2a2ec35e9d88 Mon Sep 17 00:00:00 2001 From: Eric DeVolder Date: Fri, 28 Jan 2022 15:38:08 -0500 Subject: [PATCH 253/460] ACPI ERST: bios-tables-test testcase This change implements the test suite checks for the ERST table. Signed-off-by: Eric DeVolder Reviewed-by: Ani Sinha Message-Id: <1643402289-22216-10-git-send-email-eric.devolder@oracle.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/qtest/bios-tables-test.c | 54 ++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/tests/qtest/bios-tables-test.c b/tests/qtest/bios-tables-test.c index ad536fd7b1..c4a2d1e166 100644 --- a/tests/qtest/bios-tables-test.c +++ b/tests/qtest/bios-tables-test.c @@ -1447,6 +1447,57 @@ static void test_acpi_piix4_tcg_acpi_hmat(void) test_acpi_tcg_acpi_hmat(MACHINE_PC); } +static void test_acpi_erst(const char *machine) +{ + gchar *tmp_path = g_dir_make_tmp("qemu-test-erst.XXXXXX", NULL); + gchar *params; + test_data data; + + memset(&data, 0, sizeof(data)); + data.machine = machine; + data.variant = ".acpierst"; + params = g_strdup_printf( + " -object memory-backend-file,id=erstnvram," + "mem-path=%s,size=0x10000,share=on" + " -device acpi-erst,memdev=erstnvram", tmp_path); + test_acpi_one(params, &data); + free_test_data(&data); + g_free(params); + g_assert(g_rmdir(tmp_path) == 0); + g_free(tmp_path); +} + +static void test_acpi_piix4_acpi_erst(void) +{ + test_acpi_erst(MACHINE_PC); +} + +static void test_acpi_q35_acpi_erst(void) +{ + test_acpi_erst(MACHINE_Q35); +} + +static void test_acpi_microvm_acpi_erst(void) +{ + gchar *tmp_path = g_dir_make_tmp("qemu-test-erst.XXXXXX", NULL); + gchar *params; + test_data data; + + test_acpi_microvm_prepare(&data); + data.variant = ".pcie"; + data.tcg_only = true; /* need constant host-phys-bits */ + params = g_strdup_printf(" -machine microvm," + "acpi=on,ioapic2=off,rtc=off,pcie=on" + " -object memory-backend-file,id=erstnvram," + "mem-path=%s,size=0x10000,share=on" + " -device acpi-erst,memdev=erstnvram", tmp_path); + test_acpi_one(params, &data); + g_free(params); + g_assert(g_rmdir(tmp_path) == 0); + g_free(tmp_path); + free_test_data(&data); +} + static void test_acpi_virt_tcg(void) { test_data data = { @@ -1672,6 +1723,8 @@ int main(int argc, char *argv[]) qtest_add_func("acpi/q35/dimmpxm", test_acpi_q35_tcg_dimm_pxm); qtest_add_func("acpi/piix4/acpihmat", test_acpi_piix4_tcg_acpi_hmat); qtest_add_func("acpi/q35/acpihmat", test_acpi_q35_tcg_acpi_hmat); + qtest_add_func("acpi/piix4/acpierst", test_acpi_piix4_acpi_erst); + qtest_add_func("acpi/q35/acpierst", test_acpi_q35_acpi_erst); qtest_add_func("acpi/microvm", test_acpi_microvm_tcg); qtest_add_func("acpi/microvm/usb", test_acpi_microvm_usb_tcg); qtest_add_func("acpi/microvm/rtc", test_acpi_microvm_rtc_tcg); @@ -1681,6 +1734,7 @@ int main(int argc, char *argv[]) qtest_add_func("acpi/q35/ivrs", test_acpi_q35_tcg_ivrs); if (strcmp(arch, "x86_64") == 0) { qtest_add_func("acpi/microvm/pcie", test_acpi_microvm_pcie_tcg); + qtest_add_func("acpi/microvm/acpierst", test_acpi_microvm_acpi_erst); } } if (has_kvm) { From a4752a51f1466be5010962c7044edaf763f419ef Mon Sep 17 00:00:00 2001 From: Eric DeVolder Date: Fri, 28 Jan 2022 15:38:09 -0500 Subject: [PATCH 254/460] ACPI ERST: step 6 of bios-tables-test.c Following the guidelines in tests/qtest/bios-tables-test.c, this is step 6. Below is the disassembly of tests/data/acpi/pc/ERST.acpierst. /* * Intel ACPI Component Architecture * AML/ASL+ Disassembler version 20180508 (64-bit version) * Copyright (c) 2000 - 2018 Intel Corporation * * Disassembly of tests/data/acpi/pc/ERST.acpierst, Thu Dec 2 13:32:07 2021 * * ACPI Data Table [ERST] * * Format: [HexOffset DecimalOffset ByteLength] FieldName : FieldValue */ [000h 0000 4] Signature : "ERST" [Error Record Serialization Table] [004h 0004 4] Table Length : 00000390 [008h 0008 1] Revision : 01 [009h 0009 1] Checksum : D6 [00Ah 0010 6] Oem ID : "BOCHS " [010h 0016 8] Oem Table ID : "BXPC " [018h 0024 4] Oem Revision : 00000001 [01Ch 0028 4] Asl Compiler ID : "BXPC" [020h 0032 4] Asl Compiler Revision : 00000001 [024h 0036 4] Serialization Header Length : 00000030 [028h 0040 4] Reserved : 00000000 [02Ch 0044 4] Instruction Entry Count : 0000001B [030h 0048 1] Action : 00 [Begin Write Operation] [031h 0049 1] Instruction : 03 [Write Register Value] [032h 0050 1] Flags (decoded below) : 00 Preserve Register Bits : 0 [033h 0051 1] Reserved : 00 [034h 0052 12] Register Region : [Generic Address Structure] [034h 0052 1] Space ID : 00 [SystemMemory] [035h 0053 1] Bit Width : 20 [036h 0054 1] Bit Offset : 00 [037h 0055 1] Encoded Access Width : 03 [DWord Access:32] [038h 0056 8] Address : 00000000FEBF3000 [040h 0064 8] Value : 0000000000000000 [048h 0072 8] Mask : 00000000000000FF [050h 0080 1] Action : 01 [Begin Read Operation] [051h 0081 1] Instruction : 03 [Write Register Value] [052h 0082 1] Flags (decoded below) : 00 Preserve Register Bits : 0 [053h 0083 1] Reserved : 00 [054h 0084 12] Register Region : [Generic Address Structure] [054h 0084 1] Space ID : 00 [SystemMemory] [055h 0085 1] Bit Width : 20 [056h 0086 1] Bit Offset : 00 [057h 0087 1] Encoded Access Width : 03 [DWord Access:32] [058h 0088 8] Address : 00000000FEBF3000 [060h 0096 8] Value : 0000000000000001 [068h 0104 8] Mask : 00000000000000FF [070h 0112 1] Action : 02 [Begin Clear Operation] [071h 0113 1] Instruction : 03 [Write Register Value] [072h 0114 1] Flags (decoded below) : 00 Preserve Register Bits : 0 [073h 0115 1] Reserved : 00 [074h 0116 12] Register Region : [Generic Address Structure] [074h 0116 1] Space ID : 00 [SystemMemory] [075h 0117 1] Bit Width : 20 [076h 0118 1] Bit Offset : 00 [077h 0119 1] Encoded Access Width : 03 [DWord Access:32] [078h 0120 8] Address : 00000000FEBF3000 [080h 0128 8] Value : 0000000000000002 [088h 0136 8] Mask : 00000000000000FF [090h 0144 1] Action : 03 [End Operation] [091h 0145 1] Instruction : 03 [Write Register Value] [092h 0146 1] Flags (decoded below) : 00 Preserve Register Bits : 0 [093h 0147 1] Reserved : 00 [094h 0148 12] Register Region : [Generic Address Structure] [094h 0148 1] Space ID : 00 [SystemMemory] [095h 0149 1] Bit Width : 20 [096h 0150 1] Bit Offset : 00 [097h 0151 1] Encoded Access Width : 03 [DWord Access:32] [098h 0152 8] Address : 00000000FEBF3000 [0A0h 0160 8] Value : 0000000000000003 [0A8h 0168 8] Mask : 00000000000000FF [0B0h 0176 1] Action : 04 [Set Record Offset] [0B1h 0177 1] Instruction : 02 [Write Register] [0B2h 0178 1] Flags (decoded below) : 00 Preserve Register Bits : 0 [0B3h 0179 1] Reserved : 00 [0B4h 0180 12] Register Region : [Generic Address Structure] [0B4h 0180 1] Space ID : 00 [SystemMemory] [0B5h 0181 1] Bit Width : 20 [0B6h 0182 1] Bit Offset : 00 [0B7h 0183 1] Encoded Access Width : 03 [DWord Access:32] [0B8h 0184 8] Address : 00000000FEBF3008 [0C0h 0192 8] Value : 0000000000000000 [0C8h 0200 8] Mask : 00000000FFFFFFFF [0D0h 0208 1] Action : 04 [Set Record Offset] [0D1h 0209 1] Instruction : 03 [Write Register Value] [0D2h 0210 1] Flags (decoded below) : 00 Preserve Register Bits : 0 [0D3h 0211 1] Reserved : 00 [0D4h 0212 12] Register Region : [Generic Address Structure] [0D4h 0212 1] Space ID : 00 [SystemMemory] [0D5h 0213 1] Bit Width : 20 [0D6h 0214 1] Bit Offset : 00 [0D7h 0215 1] Encoded Access Width : 03 [DWord Access:32] [0D8h 0216 8] Address : 00000000FEBF3000 [0E0h 0224 8] Value : 0000000000000004 [0E8h 0232 8] Mask : 00000000000000FF [0F0h 0240 1] Action : 05 [Execute Operation] [0F1h 0241 1] Instruction : 03 [Write Register Value] [0F2h 0242 1] Flags (decoded below) : 00 Preserve Register Bits : 0 [0F3h 0243 1] Reserved : 00 [0F4h 0244 12] Register Region : [Generic Address Structure] [0F4h 0244 1] Space ID : 00 [SystemMemory] [0F5h 0245 1] Bit Width : 20 [0F6h 0246 1] Bit Offset : 00 [0F7h 0247 1] Encoded Access Width : 03 [DWord Access:32] [0F8h 0248 8] Address : 00000000FEBF3008 [100h 0256 8] Value : 000000000000009C [108h 0264 8] Mask : 00000000000000FF [110h 0272 1] Action : 05 [Execute Operation] [111h 0273 1] Instruction : 03 [Write Register Value] [112h 0274 1] Flags (decoded below) : 00 Preserve Register Bits : 0 [113h 0275 1] Reserved : 00 [114h 0276 12] Register Region : [Generic Address Structure] [114h 0276 1] Space ID : 00 [SystemMemory] [115h 0277 1] Bit Width : 20 [116h 0278 1] Bit Offset : 00 [117h 0279 1] Encoded Access Width : 03 [DWord Access:32] [118h 0280 8] Address : 00000000FEBF3000 [120h 0288 8] Value : 0000000000000005 [128h 0296 8] Mask : 00000000000000FF [130h 0304 1] Action : 06 [Check Busy Status] [131h 0305 1] Instruction : 03 [Write Register Value] [132h 0306 1] Flags (decoded below) : 00 Preserve Register Bits : 0 [133h 0307 1] Reserved : 00 [134h 0308 12] Register Region : [Generic Address Structure] [134h 0308 1] Space ID : 00 [SystemMemory] [135h 0309 1] Bit Width : 20 [136h 0310 1] Bit Offset : 00 [137h 0311 1] Encoded Access Width : 03 [DWord Access:32] [138h 0312 8] Address : 00000000FEBF3000 [140h 0320 8] Value : 0000000000000006 [148h 0328 8] Mask : 00000000000000FF [150h 0336 1] Action : 06 [Check Busy Status] [151h 0337 1] Instruction : 01 [Read Register Value] [152h 0338 1] Flags (decoded below) : 00 Preserve Register Bits : 0 [153h 0339 1] Reserved : 00 [154h 0340 12] Register Region : [Generic Address Structure] [154h 0340 1] Space ID : 00 [SystemMemory] [155h 0341 1] Bit Width : 20 [156h 0342 1] Bit Offset : 00 [157h 0343 1] Encoded Access Width : 03 [DWord Access:32] [158h 0344 8] Address : 00000000FEBF3008 [160h 0352 8] Value : 0000000000000001 [168h 0360 8] Mask : 00000000000000FF [170h 0368 1] Action : 07 [Get Command Status] [171h 0369 1] Instruction : 03 [Write Register Value] [172h 0370 1] Flags (decoded below) : 00 Preserve Register Bits : 0 [173h 0371 1] Reserved : 00 [174h 0372 12] Register Region : [Generic Address Structure] [174h 0372 1] Space ID : 00 [SystemMemory] [175h 0373 1] Bit Width : 20 [176h 0374 1] Bit Offset : 00 [177h 0375 1] Encoded Access Width : 03 [DWord Access:32] [178h 0376 8] Address : 00000000FEBF3000 [180h 0384 8] Value : 0000000000000007 [188h 0392 8] Mask : 00000000000000FF [190h 0400 1] Action : 07 [Get Command Status] [191h 0401 1] Instruction : 00 [Read Register] [192h 0402 1] Flags (decoded below) : 00 Preserve Register Bits : 0 [193h 0403 1] Reserved : 00 [194h 0404 12] Register Region : [Generic Address Structure] [194h 0404 1] Space ID : 00 [SystemMemory] [195h 0405 1] Bit Width : 20 [196h 0406 1] Bit Offset : 00 [197h 0407 1] Encoded Access Width : 03 [DWord Access:32] [198h 0408 8] Address : 00000000FEBF3008 [1A0h 0416 8] Value : 0000000000000000 [1A8h 0424 8] Mask : 00000000000000FF [1B0h 0432 1] Action : 08 [Get Record Identifier] [1B1h 0433 1] Instruction : 03 [Write Register Value] [1B2h 0434 1] Flags (decoded below) : 00 Preserve Register Bits : 0 [1B3h 0435 1] Reserved : 00 [1B4h 0436 12] Register Region : [Generic Address Structure] [1B4h 0436 1] Space ID : 00 [SystemMemory] [1B5h 0437 1] Bit Width : 20 [1B6h 0438 1] Bit Offset : 00 [1B7h 0439 1] Encoded Access Width : 03 [DWord Access:32] [1B8h 0440 8] Address : 00000000FEBF3000 [1C0h 0448 8] Value : 0000000000000008 [1C8h 0456 8] Mask : 00000000000000FF [1D0h 0464 1] Action : 08 [Get Record Identifier] [1D1h 0465 1] Instruction : 00 [Read Register] [1D2h 0466 1] Flags (decoded below) : 00 Preserve Register Bits : 0 [1D3h 0467 1] Reserved : 00 [1D4h 0468 12] Register Region : [Generic Address Structure] [1D4h 0468 1] Space ID : 00 [SystemMemory] [1D5h 0469 1] Bit Width : 40 [1D6h 0470 1] Bit Offset : 00 [1D7h 0471 1] Encoded Access Width : 04 [QWord Access:64] [1D8h 0472 8] Address : 00000000FEBF3008 [1E0h 0480 8] Value : 0000000000000000 [1E8h 0488 8] Mask : FFFFFFFFFFFFFFFF [1F0h 0496 1] Action : 09 [Set Record Identifier] [1F1h 0497 1] Instruction : 02 [Write Register] [1F2h 0498 1] Flags (decoded below) : 00 Preserve Register Bits : 0 [1F3h 0499 1] Reserved : 00 [1F4h 0500 12] Register Region : [Generic Address Structure] [1F4h 0500 1] Space ID : 00 [SystemMemory] [1F5h 0501 1] Bit Width : 40 [1F6h 0502 1] Bit Offset : 00 [1F7h 0503 1] Encoded Access Width : 04 [QWord Access:64] [1F8h 0504 8] Address : 00000000FEBF3008 [200h 0512 8] Value : 0000000000000000 [208h 0520 8] Mask : FFFFFFFFFFFFFFFF [210h 0528 1] Action : 09 [Set Record Identifier] [211h 0529 1] Instruction : 03 [Write Register Value] [212h 0530 1] Flags (decoded below) : 00 Preserve Register Bits : 0 [213h 0531 1] Reserved : 00 [214h 0532 12] Register Region : [Generic Address Structure] [214h 0532 1] Space ID : 00 [SystemMemory] [215h 0533 1] Bit Width : 20 [216h 0534 1] Bit Offset : 00 [217h 0535 1] Encoded Access Width : 03 [DWord Access:32] [218h 0536 8] Address : 00000000FEBF3000 [220h 0544 8] Value : 0000000000000009 [228h 0552 8] Mask : 00000000000000FF [230h 0560 1] Action : 0A [Get Record Count] [231h 0561 1] Instruction : 03 [Write Register Value] [232h 0562 1] Flags (decoded below) : 00 Preserve Register Bits : 0 [233h 0563 1] Reserved : 00 [234h 0564 12] Register Region : [Generic Address Structure] [234h 0564 1] Space ID : 00 [SystemMemory] [235h 0565 1] Bit Width : 20 [236h 0566 1] Bit Offset : 00 [237h 0567 1] Encoded Access Width : 03 [DWord Access:32] [238h 0568 8] Address : 00000000FEBF3000 [240h 0576 8] Value : 000000000000000A [248h 0584 8] Mask : 00000000000000FF [250h 0592 1] Action : 0A [Get Record Count] [251h 0593 1] Instruction : 00 [Read Register] [252h 0594 1] Flags (decoded below) : 00 Preserve Register Bits : 0 [253h 0595 1] Reserved : 00 [254h 0596 12] Register Region : [Generic Address Structure] [254h 0596 1] Space ID : 00 [SystemMemory] [255h 0597 1] Bit Width : 20 [256h 0598 1] Bit Offset : 00 [257h 0599 1] Encoded Access Width : 03 [DWord Access:32] [258h 0600 8] Address : 00000000FEBF3008 [260h 0608 8] Value : 0000000000000000 [268h 0616 8] Mask : 00000000FFFFFFFF [270h 0624 1] Action : 0B [Begin Dummy Write] [271h 0625 1] Instruction : 03 [Write Register Value] [272h 0626 1] Flags (decoded below) : 00 Preserve Register Bits : 0 [273h 0627 1] Reserved : 00 [274h 0628 12] Register Region : [Generic Address Structure] [274h 0628 1] Space ID : 00 [SystemMemory] [275h 0629 1] Bit Width : 20 [276h 0630 1] Bit Offset : 00 [277h 0631 1] Encoded Access Width : 03 [DWord Access:32] [278h 0632 8] Address : 00000000FEBF3000 [280h 0640 8] Value : 000000000000000B [288h 0648 8] Mask : 00000000000000FF [290h 0656 1] Action : 0D [Get Error Address Range] [291h 0657 1] Instruction : 03 [Write Register Value] [292h 0658 1] Flags (decoded below) : 00 Preserve Register Bits : 0 [293h 0659 1] Reserved : 00 [294h 0660 12] Register Region : [Generic Address Structure] [294h 0660 1] Space ID : 00 [SystemMemory] [295h 0661 1] Bit Width : 20 [296h 0662 1] Bit Offset : 00 [297h 0663 1] Encoded Access Width : 03 [DWord Access:32] [298h 0664 8] Address : 00000000FEBF3000 [2A0h 0672 8] Value : 000000000000000D [2A8h 0680 8] Mask : 00000000000000FF [2B0h 0688 1] Action : 0D [Get Error Address Range] [2B1h 0689 1] Instruction : 00 [Read Register] [2B2h 0690 1] Flags (decoded below) : 00 Preserve Register Bits : 0 [2B3h 0691 1] Reserved : 00 [2B4h 0692 12] Register Region : [Generic Address Structure] [2B4h 0692 1] Space ID : 00 [SystemMemory] [2B5h 0693 1] Bit Width : 40 [2B6h 0694 1] Bit Offset : 00 [2B7h 0695 1] Encoded Access Width : 04 [QWord Access:64] [2B8h 0696 8] Address : 00000000FEBF3008 [2C0h 0704 8] Value : 0000000000000000 [2C8h 0712 8] Mask : FFFFFFFFFFFFFFFF [2D0h 0720 1] Action : 0E [Get Error Address Length] [2D1h 0721 1] Instruction : 03 [Write Register Value] [2D2h 0722 1] Flags (decoded below) : 00 Preserve Register Bits : 0 [2D3h 0723 1] Reserved : 00 [2D4h 0724 12] Register Region : [Generic Address Structure] [2D4h 0724 1] Space ID : 00 [SystemMemory] [2D5h 0725 1] Bit Width : 20 [2D6h 0726 1] Bit Offset : 00 [2D7h 0727 1] Encoded Access Width : 03 [DWord Access:32] [2D8h 0728 8] Address : 00000000FEBF3000 [2E0h 0736 8] Value : 000000000000000E [2E8h 0744 8] Mask : 00000000000000FF [2F0h 0752 1] Action : 0E [Get Error Address Length] [2F1h 0753 1] Instruction : 00 [Read Register] [2F2h 0754 1] Flags (decoded below) : 00 Preserve Register Bits : 0 [2F3h 0755 1] Reserved : 00 [2F4h 0756 12] Register Region : [Generic Address Structure] [2F4h 0756 1] Space ID : 00 [SystemMemory] [2F5h 0757 1] Bit Width : 40 [2F6h 0758 1] Bit Offset : 00 [2F7h 0759 1] Encoded Access Width : 04 [QWord Access:64] [2F8h 0760 8] Address : 00000000FEBF3008 [300h 0768 8] Value : 0000000000000000 [308h 0776 8] Mask : 00000000FFFFFFFF [310h 0784 1] Action : 0F [Get Error Attributes] [311h 0785 1] Instruction : 03 [Write Register Value] [312h 0786 1] Flags (decoded below) : 00 Preserve Register Bits : 0 [313h 0787 1] Reserved : 00 [314h 0788 12] Register Region : [Generic Address Structure] [314h 0788 1] Space ID : 00 [SystemMemory] [315h 0789 1] Bit Width : 20 [316h 0790 1] Bit Offset : 00 [317h 0791 1] Encoded Access Width : 03 [DWord Access:32] [318h 0792 8] Address : 00000000FEBF3000 [320h 0800 8] Value : 000000000000000F [328h 0808 8] Mask : 00000000000000FF [330h 0816 1] Action : 0F [Get Error Attributes] [331h 0817 1] Instruction : 00 [Read Register] [332h 0818 1] Flags (decoded below) : 00 Preserve Register Bits : 0 [333h 0819 1] Reserved : 00 [334h 0820 12] Register Region : [Generic Address Structure] [334h 0820 1] Space ID : 00 [SystemMemory] [335h 0821 1] Bit Width : 20 [336h 0822 1] Bit Offset : 00 [337h 0823 1] Encoded Access Width : 03 [DWord Access:32] [338h 0824 8] Address : 00000000FEBF3008 [340h 0832 8] Value : 0000000000000000 [348h 0840 8] Mask : 00000000FFFFFFFF [350h 0848 1] Action : 10 [Execute Timings] [351h 0849 1] Instruction : 03 [Write Register Value] [352h 0850 1] Flags (decoded below) : 00 Preserve Register Bits : 0 [353h 0851 1] Reserved : 00 [354h 0852 12] Register Region : [Generic Address Structure] [354h 0852 1] Space ID : 00 [SystemMemory] [355h 0853 1] Bit Width : 20 [356h 0854 1] Bit Offset : 00 [357h 0855 1] Encoded Access Width : 03 [DWord Access:32] [358h 0856 8] Address : 00000000FEBF3000 [360h 0864 8] Value : 0000000000000010 [368h 0872 8] Mask : 00000000000000FF [370h 0880 1] Action : 10 [Execute Timings] [371h 0881 1] Instruction : 00 [Read Register] [372h 0882 1] Flags (decoded below) : 00 Preserve Register Bits : 0 [373h 0883 1] Reserved : 00 [374h 0884 12] Register Region : [Generic Address Structure] [374h 0884 1] Space ID : 00 [SystemMemory] [375h 0885 1] Bit Width : 40 [376h 0886 1] Bit Offset : 00 [377h 0887 1] Encoded Access Width : 04 [QWord Access:64] [378h 0888 8] Address : 00000000FEBF3008 [380h 0896 8] Value : 0000000000000000 [388h 0904 8] Mask : FFFFFFFFFFFFFFFF Raw Table Data: Length 912 (0x390) Note that the contents of tests/data/q35/ERST.acpierst and tests/data/microvm/ERST.pcie are the same except for differences due to assigned base address. Files tests/data/pc/DSDT.acpierst and tests/data/acpi/q35/DSDT.acpierst are new files (and are included as a result of 'make check' process). Rather than provide the entire content, I am providing the differences between pc/DSDT and pc/DSDT.acpierst, and the difference between q35/DSDT and q35/DSDT.acpierst, with an explanation to follow. diff pc/DSDT pc/DSDT.acpierst: @@ -5,13 +5,13 @@ * * Disassembling to symbolic ASL+ operators * - * Disassembly of tests/data/acpi/pc/DSDT, Thu Dec 2 10:10:13 2021 + * Disassembly of tests/data/acpi/pc/DSDT.acpierst, Thu Dec 2 12:59:36 2021 * * Original Table Header: * Signature "DSDT" - * Length 0x00001772 (6002) + * Length 0x00001751 (5969) * Revision 0x01 **** 32-bit table (V1), no 64-bit math support - * Checksum 0x9E + * Checksum 0x95 * OEM ID "BOCHS " * OEM Table ID "BXPC " * OEM Revision 0x00000001 (1) @@ -964,16 +964,11 @@ DefinitionBlock ("", "DSDT", 1, "BOCHS " Device (S18) { - Name (_SUN, 0x03) // _SUN: Slot User Number Name (_ADR, 0x00030000) // _ADR: Address - Method (_EJ0, 1, NotSerialized) // _EJx: Eject Device - { - PCEJ (BSEL, _SUN) - } - + Name (ASUN, 0x03) Method (_DSM, 4, Serialized) // _DSM: Device-Specific Method { - Return (PDSM (Arg0, Arg1, Arg2, Arg3, BSEL, _SUN)) + Return (PDSM (Arg0, Arg1, Arg2, Arg3, BSEL, ASUN)) } } @@ -1399,11 +1394,6 @@ DefinitionBlock ("", "DSDT", 1, "BOCHS " Method (DVNT, 2, NotSerialized) { - If ((Arg0 & 0x08)) - { - Notify (S18, Arg1) - } - If ((Arg0 & 0x10)) { Notify (S20, Arg1) diff q35/DSDT and q35/DSDT.acpierst: @@ -5,13 +5,13 @@ * * Disassembling to symbolic ASL+ operators * - * Disassembly of tests/data/acpi/q35/DSDT, Thu Dec 2 10:10:13 2021 + * Disassembly of tests/data/acpi/q35/DSDT.acpierst, Thu Dec 2 12:59:36 2021 * * Original Table Header: * Signature "DSDT" - * Length 0x00002061 (8289) + * Length 0x00002072 (8306) * Revision 0x01 **** 32-bit table (V1), no 64-bit math support - * Checksum 0xFA + * Checksum 0x9A * OEM ID "BOCHS " * OEM Table ID "BXPC " * OEM Revision 0x00000001 (1) @@ -3278,6 +3278,11 @@ DefinitionBlock ("", "DSDT", 1, "BOCHS " } } + Device (S10) + { + Name (_ADR, 0x00020000) // _ADR: Address + } + Method (PCNT, 0, NotSerialized) { } For both pc and q35, there is but a small difference between this DSDT.acpierst and the corresponding DSDT. In both cases, the changes occur under the hiearchy: Scope (\_SB) { Scope (PCI0) { which leads me to believe that the change to the DSDT was needed due to the introduction of the ERST PCI device. And is explained in detail by Ani Sinha: I have convinced myself of the changes we see in the DSDT tables. On i440fx side, we are adding a non-hotpluggable pci device on slot 3. So the changes we see are basically replacing an empty hotpluggable slot on the pci root port with a non-hotplugggable device. On q35, bsel on pcie root bus is not set (its not hotpluggable bus), so the change basically adds the address enumeration for the device. Signed-off-by: Eric DeVolder Acked-by: Ani Sinha Message-Id: <1643402289-22216-11-git-send-email-eric.devolder@oracle.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/data/acpi/microvm/ERST.pcie | Bin 0 -> 912 bytes tests/data/acpi/pc/DSDT.acpierst | Bin 0 -> 5969 bytes tests/data/acpi/pc/ERST.acpierst | Bin 0 -> 912 bytes tests/data/acpi/q35/DSDT.acpierst | Bin 0 -> 8306 bytes tests/data/acpi/q35/ERST.acpierst | Bin 0 -> 912 bytes tests/qtest/bios-tables-test-allowed-diff.h | 5 ----- 6 files changed, 5 deletions(-) diff --git a/tests/data/acpi/microvm/ERST.pcie b/tests/data/acpi/microvm/ERST.pcie index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..a6d0cb783831ebc18972ec57bb6c624438ff150d 100644 GIT binary patch literal 912 zcmaKoTMmLS5Jd;doACJehb6cK12OSWBYwCn7ocm^-rA|;CUz1YmosPDa=fm$hY?9$ z^LaU~(|o@yldVKV@Q&x+UZ@>zwpS)GZ(sO?Lc}v64j-jFC7yn9;D$INO8pFiUB7f+ zf49KN&wPvW+;jDxe>nP4Iq`z#7tC?s&HniOCHcA!tc6i7Z+t&KoWCN!@(t>{e2`4% zZhiFB_p5lnFb*Ui&1&BGpsO$pT3_)pk?J?$-jLiezjJ$6`=r)uYry0Rn7KuJIp&j)|! zckaizM=~sYzU*-I~PkUtAn>Dr`~u=;o(_O$t$PK8~R}U#&P6`{0u*{ zz`m8fl|Wu#ucTKJu-TjNQ`rN~%rBccG0yWwF_}_zQl+X71|-_g)Pr`x1*Xgb!&xdP4yUPQ>QealcAR5Lj8F@vR~&d_hFrfVBOumUtb3< zL8GI#8|W0leXv|!GGL=~vE5*uM7z%Af!czNXYqlQL#IT$!9xR0zORu68XY#=M-SGy z3b+$tZv(*Hu4BBt4F>MUo>Padde^ZZU%V_4TiQ&t&6ruaomTLcp<9-2bBZ=qyp40+ ziEQ&$6L)c>%cI)0;%&VWL5E5@F~Or>jX}g)!VlmH>3A6L#ZGj;i8(jvxl3w$XL%gc z#4Y0Q*ces>sy7Obm6bTnr@oqih!n=P&+!w*&jZjVy;9R95=2i+)QqA9kLa1VJk2ES zOh6C4;>puBt75SyO`ippr%I9Z{pk6j=(st4aP-WP=ov412KgP0p3z1}&)7R9%3U9d z&O-shXOD!>df~G};j<@%&-TL`!{M=J{^0EAj)b4{!p{wbpF1J^oV~LmWA@f?c-apx zmJl5aIOP$4%5j~Id6TV0{V^u0sG`|bSAOB2V2gb1@Ki7>g+T~D<}I$cZmy;lKl#kP zxAm*k{f|HWWb1z8<4?h6y1Bg6FLiOw7Z@DC0gGYf$3^AUwgVosAOD1e9Hex!P-c3u zY%#r3y2G$SrRg@$K+^S+fmJX`o|0}AmQ(0%hN|GcJ*{Ry{RpZVR_9(>4G!#sMm9K*-Kb2oqvI?7dL24<1WVww+F6k zrAwi@3blP`ov2|RQA!4yn|HMt+&l|rp;ky|p}*s4Bhi{tS7b7IwWYtO z($_%y0DUt+12O0t{g&d0!qm$i(>FX+UGsM;osE9mca!r!@5Ld6Jt2$-nabyyC?4*zT@l2 z4LgzF3U^-%E&8T*RhMxH{B|{Nmd$hyhrlm@q(4&m{QfcO=jBztZw|gWU^0Q+lFdc4 z;t%ATAUG5ws_1bncmXfi8SEC{UBmF!TrD{!GvtiVS87Q`#ts;JTa>Png~+QvKOAg( z=l%H)^?9Gb?Lui47fY7Bv8_MKSPiRTs@3YkU)Q{@YvnKAT;;J8U>fgQ>9qeF0+gJ! zS_V9pumOy9sSU)%^lS1X17&Pw`o1`MA>W^ePI^H@E*S%`|HOx^c$lLHVkhwcpCba771jDZ#SdZmhM`3K=q{% zNdudV5*`|Tn?lmSM!~R<_iYsI+Q3HPu(0YhlR~GImZVAKI~h z+O@D02|KpVC?DEYlCeYxd&-j!?Kerrtt;#;Pd>DJ8RM1`cI=OKE&Yg{vc{!8uv6Z~ u5j$m$OK@OMk$l8{6J=Z)1AB{Pv}@<7F~|QN>AydkHSF$IS^vS{(*FTuhT2R3 literal 0 HcmV?d00001 diff --git a/tests/data/acpi/pc/ERST.acpierst b/tests/data/acpi/pc/ERST.acpierst index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..7965ac2562e7b23bf1dc2caaf00ef66453c4f47a 100644 GIT binary patch literal 912 zcmaKoOAdlC6h$9Upg{N}4xO+BmS7+z&NSk{Ww`+N;?C6GO0l^KeF>#Er>E`f@jBlg ziAb~?&(mq{$NOdKO+_MtIsSwBP(BS` zh6Ua)#A*M6_AiN-%#j24ugI^+uZh>pkpuT{$ZyEEIpDYCx8&O#=&vKcBj4u0`Con^ B!2tjO literal 0 HcmV?d00001 diff --git a/tests/data/acpi/q35/DSDT.acpierst b/tests/data/acpi/q35/DSDT.acpierst index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..cad26e3f0c27a40a33101155a5282ed9bcb1d441 100644 GIT binary patch literal 8306 zcmb7JOKcm*8J^`!tL0K!Qk3OaY{E(UN|8`@UTx8!W+{ zr+@MJrMD_~fBw&3-hS0e0dO9FP5iwO(II>Q|7$G zoVF=c*370~>q(Ucw2`X*UeoWjna$X}Z?i1bzv)oC^kS{!mHe(ZC>5U_1icE2s3nZ3 zY5U#Ka>9}Q$uMNYFgqR}u+hRN!)^AvZJYi3f9V5uU@eNi<*kJe=czCEzGW%K(3|f) zNxh-*?(d&$1(g9GVw69NuK2C)X2J?B+gV$DDk*`4>hC|jR`mcD_b7?pqgS%bacj9@ zge+u+(J-#EtN42^folEz$J!fPhpfdVjb&Qtb2LKz{qyU7Z&l__iTiD6SjWmWjJK0- zCr$I*9?yK<-Dn?j(m-Q0XK?N(?n{28O7Ue=tUS zz8mz6>|NV3acd}WF?L%e9K2G0FQ{F_Ecm-^;l^btaI59oMO>(M+Fc`iPtYiEtDUWO z$!~ctmHI|?-CbkF$9=bUG0}gNqVYJ|B}DX&ag9-q{`aZ*fEAypfHMn4xgt7*F*YoE z8Q%-q3#0XTYTSG7AO8uSdAI$^%Gd1^e{CtL5V)tERpa9(Exo7$}ef`$x|2%4;rOQ9c_Ndgx~!~$?ZT;n`I zWk^iq=hy^i0vkufl+Xm@Oh7pkAu-V#j!kl=>_L(dQ$mx12`FbGB&Mu_c_pwsPl_`o zG$oj_^9WU)X-#LEGbJ=Fn6mQ-Rh=14XNEH+G^6W8sOp^4bk1oy=X9M2Rh_1$)6{gD zx=w_uPP~DXk)GFd&g(i6syZ!Ar={t%be#xQomow1R@0f)bs|)C;$5iRPg~Pz>pBst zI&+%NoTf9U>qMyPT+nnbXgU{kod{K(i<-_wP3NMn6QQcp(R4bRPDj^?P}P~&bmldk zd0i($RcArdSiBQ#f zOw)Nx(|JtSiBQ#vF9|$-@;$Vo>0HruB2;xA*K{7&bRO4rB2;yr;LJ+=COpBJ)%az1 zLNMJ2Au+ueds1Vb)R-rACPI~YN@JeVn5T3mLX~-1W1iNSr*$SmmHCLqd_-eDqB9Yy z%rl%RUFHmD%6I%3!IUp2Z%2?G^a&V+#~P%=;f zu&LjgxD0RX>1(q{mpbC@>R6seC3>2Z%2?G^a&V+#~P%=;f=3{*fllMEE0)CmI> zSk8ojDo`>|0p(0GP=rz^3{+q_69%e4$v_2^Gs!>^N}VuJf#pmXr~)Me6;RG314Srx z!axOM_lrzad z5lWpfP=V!47^ngz0~JutBm+e#b;3XemNQ|X3X}{~Ksl2P6rt1!0~J`#gn=qhGEf2K zOfpb}QYQ>lU^x>8szAv=1(Y+%KoLrvFi=E_fg(~36rpOM2o(cWm@rUrJ|DqSSTCWL;la|0sTXICqsWK z{hL31DoOvP(y9incNl(mvjWH6tkS`ygJ&|Vy=>O#yiSJ(9hzowskMpKfYJPW*4{1_ zS>_L{Z1L&VrrVtdpj*x_SlN#=Y@V+Ky~A!(o0Z0~6}`7>EiyVoC%RaAS)aocbd=l=)@cw3a6JzzFxX9Om4yX(0O{JJ0c&&iFPv_PiZ&{@E zC&pkx>rc%Di%Qq*=H;*XaMEMlG$|lP@qP%>0;IjhLU98{gZN&_q^7upZI!-q^ zjjg<%4&NG6=|tPibW`)Ppr^wJ$W%JfHZ$GSJi`(&%`+(sUnNuNMBB`CQ}d>%r^Bbp zR65Z%Gu_lYSk}|w`(-MfXq%aC>bX9qr^83hR65Z%Gu_m4x}vAUm(El=(Wa+!PCZ!9 z-d_ybRcy6jTg6Ud&EMI0LF`U6t;-Kq{QYh%kWZXv#f^kmHm}%*jUwvC(+XRSGHyMZ~lOS3M;vOtz)>YPGGp* z_KOAptfED+^h6TSZi)|T4uzk5l{Vv%UlmSBs!U^xPGf# zUM*8M#Kuv&n0I^imlGpvxXV6t%jNR5Vl#u)`^tR((aTSz}YjKG}?75er8r zhm#jbLS!vs*?sED`r1KQY^O=q!xb_nQz665{Kfh)H*h=V5i(4-Q@d11j3c%a8CSpX z3d5^L#I8n07_sdy*6rU8L*ug@G=6ym-#}Cs3&$W3#Pd8>jyVA>NEt6rjfxbhF3)nR6Wl0R%y>`8L3X`Yd*9Nunc^1!j znn?6aIU6au#3z0{r^?T=;!l8GWi$|nJAz`zN@?)j4)3XkaN5gart(c8zE^rNYyIU` zjwP@}fZv!7*fG3s-?t4D?FQaS_wL6`E~j%9*^m>n0^VSZNHk> z#`=@E!v3gTxr=?-Z^f0nFWi0tTefjF(MF?1dtewi?ME?fQo1P&j@YHn(=;}&UM$mYa7S0P5HyiC?8xH z6f!9TeaHFJgMyi|5*8Nm5IsXzNQv{a2e+ZW|787=D||1vS!-|MF*;9ZZ04#xcG6Co z-r~+i`ZC|zYozHe_V=?vxh(dKF(f|<%Fl}ccg$cg<6zZe*tEpSd3kbyPWmvO)tCru Lf)z9L%&`9h*%QBN literal 0 HcmV?d00001 diff --git a/tests/data/acpi/q35/ERST.acpierst b/tests/data/acpi/q35/ERST.acpierst index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..7965ac2562e7b23bf1dc2caaf00ef66453c4f47a 100644 GIT binary patch literal 912 zcmaKoOAdlC6h$9Upg{N}4xO+BmS7+z&NSk{Ww`+N;?C6GO0l^KeF>#Er>E`f@jBlg ziAb~?&(mq{$NOdKO+_MtIsSwBP(BS` zh6Ua)#A*M6_AiN-%#j24ugI^+uZh>pkpuT{$ZyEEIpDYCx8&O#=&vKcBj4u0`Con^ B!2tjO 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 603db07711..dfb8523c8b 100644 --- a/tests/qtest/bios-tables-test-allowed-diff.h +++ b/tests/qtest/bios-tables-test-allowed-diff.h @@ -1,6 +1 @@ /* List of comma-separated changed AML files to ignore */ -"tests/data/acpi/pc/DSDT.acpierst", -"tests/data/acpi/pc/ERST.acpierst", -"tests/data/acpi/q35/DSDT.acpierst", -"tests/data/acpi/q35/ERST.acpierst", -"tests/data/acpi/microvm/ERST.pcie", From dd4fc6058557cd2a9e23a37da44d054f724ca3e8 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Tue, 11 Jan 2022 13:08:30 +0100 Subject: [PATCH 255/460] util/oslib-posix: Fix missing unlock in the error path of os_mem_prealloc() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We're missing an unlock in case installing the signal handler failed. Fortunately, we barely see this error in real life. Fixes: a960d6642d39 ("util/oslib-posix: Support concurrent os_mem_prealloc() invocation") Fixes: CID 1468941 Cc: Paolo Bonzini Cc: Michael S. Tsirkin Cc: Pankaj Gupta Cc: Daniel P. Berrangé Cc: Michal Privoznik Signed-off-by: David Hildenbrand Message-Id: <20220111120830.119912-1-david@redhat.com> Reviewed-by: Pankaj Gupta Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- util/oslib-posix.c | 1 + 1 file changed, 1 insertion(+) diff --git a/util/oslib-posix.c b/util/oslib-posix.c index 9efdc74bba..ac0dbc2adc 100644 --- a/util/oslib-posix.c +++ b/util/oslib-posix.c @@ -683,6 +683,7 @@ void os_mem_prealloc(int fd, char *area, size_t memory, int smp_cpus, ret = sigaction(SIGBUS, &act, &sigbus_oldact); if (ret) { + qemu_mutex_unlock(&sigbus_mutex); error_setg_errno(errp, errno, "os_mem_prealloc: failed to install signal handler"); return; From 63888fa78be5825647ce1187dcd7b2470d8bb452 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Feb 2022 17:04:42 +0000 Subject: [PATCH 256/460] target/arm: Fix sve_zcr_len_for_el for VHE mode running When HCR_EL2.{E2H,TGE} == '11', ZCR_EL1 is unused. Reported-by: Zenghui Yu Signed-off-by: Richard Henderson Reviewed-by: Peter Maydell Reviewed-by: Zenghui Yu Message-id: 20220127063428.30212-2-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index 6dd241fbef..649958a727 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -6225,7 +6225,8 @@ uint32_t sve_zcr_len_for_el(CPUARMState *env, int el) ARMCPU *cpu = env_archcpu(env); uint32_t zcr_len = cpu->sve_max_vq - 1; - if (el <= 1) { + if (el <= 1 && + (arm_hcr_el2_eff(env) & (HCR_E2H | HCR_TGE)) != (HCR_E2H | HCR_TGE)) { zcr_len = MIN(zcr_len, 0xf & (uint32_t)env->vfp.zcr_el[1]); } if (el <= 2 && arm_feature(env, ARM_FEATURE_EL2)) { From 7701cee545109f737d43c73ac9d8be2523b9d54c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Feb 2022 17:04:43 +0000 Subject: [PATCH 257/460] target/arm: Tidy sve_exception_el for CPACR_EL1 access Extract entire fields for ZEN and FPEN, rather than testing specific bits. This makes it easier to follow the code versus the ARM spec. Signed-off-by: Richard Henderson Reviewed-by: Peter Maydell Reviewed-by: Zenghui Yu Message-id: 20220127063428.30212-3-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper.c | 36 +++++++++++++++++------------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index 649958a727..e474ab2e1d 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -6154,30 +6154,28 @@ int sve_exception_el(CPUARMState *env, int el) uint64_t hcr_el2 = arm_hcr_el2_eff(env); if (el <= 1 && (hcr_el2 & (HCR_E2H | HCR_TGE)) != (HCR_E2H | HCR_TGE)) { - bool disabled = false; - - /* The CPACR.ZEN controls traps to EL1: - * 0, 2 : trap EL0 and EL1 accesses - * 1 : trap only EL0 accesses - * 3 : trap no accesses - */ - if (!extract32(env->cp15.cpacr_el1, 16, 1)) { - disabled = true; - } else if (!extract32(env->cp15.cpacr_el1, 17, 1)) { - disabled = el == 0; - } - if (disabled) { + /* Check CPACR.ZEN. */ + switch (extract32(env->cp15.cpacr_el1, 16, 2)) { + case 1: + if (el != 0) { + break; + } + /* fall through */ + case 0: + case 2: /* route_to_el2 */ return hcr_el2 & HCR_TGE ? 2 : 1; } /* Check CPACR.FPEN. */ - if (!extract32(env->cp15.cpacr_el1, 20, 1)) { - disabled = true; - } else if (!extract32(env->cp15.cpacr_el1, 21, 1)) { - disabled = el == 0; - } - if (disabled) { + switch (extract32(env->cp15.cpacr_el1, 20, 2)) { + case 1: + if (el != 0) { + break; + } + /* fall through */ + case 0: + case 2: return 0; } } From d5a6fa2dcf9ea66cc5d0852ebeb4861423758be0 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Feb 2022 17:04:43 +0000 Subject: [PATCH 258/460] target/arm: Fix {fp, sve}_exception_el for VHE mode running When HCR_EL2.E2H is set, the format of CPTR_EL2 changes to look more like CPACR_EL1, with ZEN and FPEN fields instead of TZ and TFP fields. Reported-by: Zenghui Yu Signed-off-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20220127063428.30212-4-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper.c | 77 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 60 insertions(+), 17 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index e474ab2e1d..83bbb446e7 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -6180,15 +6180,41 @@ int sve_exception_el(CPUARMState *env, int el) } } - /* CPTR_EL2. Since TZ and TFP are positive, - * they will be zero when EL2 is not present. + /* + * CPTR_EL2 changes format with HCR_EL2.E2H (regardless of TGE). */ - if (el <= 2 && arm_is_el2_enabled(env)) { - if (env->cp15.cptr_el[2] & CPTR_TZ) { - return 2; - } - if (env->cp15.cptr_el[2] & CPTR_TFP) { - return 0; + if (el <= 2) { + if (hcr_el2 & HCR_E2H) { + /* Check CPTR_EL2.ZEN. */ + switch (extract32(env->cp15.cptr_el[2], 16, 2)) { + case 1: + if (el != 0 || !(hcr_el2 & HCR_TGE)) { + break; + } + /* fall through */ + case 0: + case 2: + return 2; + } + + /* Check CPTR_EL2.FPEN. */ + switch (extract32(env->cp15.cptr_el[2], 20, 2)) { + case 1: + if (el == 2 || !(hcr_el2 & HCR_TGE)) { + break; + } + /* fall through */ + case 0: + case 2: + return 0; + } + } else if (arm_is_el2_enabled(env)) { + if (env->cp15.cptr_el[2] & CPTR_TZ) { + return 2; + } + if (env->cp15.cptr_el[2] & CPTR_TFP) { + return 0; + } } } @@ -12912,6 +12938,8 @@ uint32_t HELPER(crc32c)(uint32_t acc, uint32_t val, uint32_t bytes) int fp_exception_el(CPUARMState *env, int cur_el) { #ifndef CONFIG_USER_ONLY + uint64_t hcr_el2; + /* CPACR and the CPTR registers don't exist before v6, so FP is * always accessible */ @@ -12935,13 +12963,15 @@ int fp_exception_el(CPUARMState *env, int cur_el) return 0; } + hcr_el2 = arm_hcr_el2_eff(env); + /* The CPACR controls traps to EL1, or PL1 if we're 32 bit: * 0, 2 : trap EL0 and EL1/PL1 accesses * 1 : trap only EL0 accesses * 3 : trap no accesses * This register is ignored if E2H+TGE are both set. */ - if ((arm_hcr_el2_eff(env) & (HCR_E2H | HCR_TGE)) != (HCR_E2H | HCR_TGE)) { + if ((hcr_el2 & (HCR_E2H | HCR_TGE)) != (HCR_E2H | HCR_TGE)) { int fpen = extract32(env->cp15.cpacr_el1, 20, 2); switch (fpen) { @@ -12982,15 +13012,28 @@ int fp_exception_el(CPUARMState *env, int cur_el) } } - /* For the CPTR registers we don't need to guard with an ARM_FEATURE - * check because zero bits in the registers mean "don't trap". + /* + * CPTR_EL2 is present in v7VE or v8, and changes format + * with HCR_EL2.E2H (regardless of TGE). */ - - /* CPTR_EL2 : present in v7VE or v8 */ - if (cur_el <= 2 && extract32(env->cp15.cptr_el[2], 10, 1) - && arm_is_el2_enabled(env)) { - /* Trap FP ops at EL2, NS-EL1 or NS-EL0 to EL2 */ - return 2; + if (cur_el <= 2) { + if (hcr_el2 & HCR_E2H) { + /* Check CPTR_EL2.FPEN. */ + switch (extract32(env->cp15.cptr_el[2], 20, 2)) { + case 1: + if (cur_el != 0 || !(hcr_el2 & HCR_TGE)) { + break; + } + /* fall through */ + case 0: + case 2: + return 2; + } + } else if (arm_is_el2_enabled(env)) { + if (env->cp15.cptr_el[2] & CPTR_TFP) { + return 2; + } + } } /* CPTR_EL3 : present in v8 */ From a7b66ada6e88aa9a9f420f1116569b2df47fa1ab Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Feb 2022 17:04:43 +0000 Subject: [PATCH 259/460] target/arm: Use CPTR_TFP with CPTR_EL3 in fp_exception_el Use the named bit rather than a bare extract32. Signed-off-by: Richard Henderson Reviewed-by: Peter Maydell Reviewed-by: Zenghui Yu Message-id: 20220127063428.30212-5-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- 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 83bbb446e7..b5f80988c9 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -13037,7 +13037,7 @@ int fp_exception_el(CPUARMState *env, int cur_el) } /* CPTR_EL3 : present in v8 */ - if (extract32(env->cp15.cptr_el[3], 10, 1)) { + if (env->cp15.cptr_el[3] & CPTR_TFP) { /* Trap all FP ops to EL3 */ return 3; } From c74ccb5dd6955736907256aab8229f63385a5e2e Mon Sep 17 00:00:00 2001 From: Francisco Iglesias Date: Thu, 3 Feb 2022 15:17:42 +0000 Subject: [PATCH 260/460] hw/arm/xlnx-zynqmp: 'Or' the QSPI / QSPI DMA IRQs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 'Or' the IRQs coming from the QSPI and QSPI DMA models. This is done for avoiding the situation where one of the models incorrectly deasserts an interrupt asserted from the other model (which will result in that the IRQ is lost and will not reach guest SW). Signed-off-by: Francisco Iglesias Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Luc Michel Message-id: 20220203151742.1457-1-francisco.iglesias@xilinx.com Signed-off-by: Peter Maydell --- hw/arm/xlnx-zynqmp.c | 14 ++++++++++++-- include/hw/arm/xlnx-zynqmp.h | 2 ++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/hw/arm/xlnx-zynqmp.c b/hw/arm/xlnx-zynqmp.c index 1c52a575aa..5fbf38c466 100644 --- a/hw/arm/xlnx-zynqmp.c +++ b/hw/arm/xlnx-zynqmp.c @@ -50,6 +50,7 @@ #define LQSPI_ADDR 0xc0000000 #define QSPI_IRQ 15 #define QSPI_DMA_ADDR 0xff0f0800 +#define NUM_QSPI_IRQ_LINES 2 #define DP_ADDR 0xfd4a0000 #define DP_IRQ 113 @@ -362,6 +363,8 @@ static void xlnx_zynqmp_init(Object *obj) } object_initialize_child(obj, "qspi-dma", &s->qspi_dma, TYPE_XLNX_CSU_DMA); + object_initialize_child(obj, "qspi-irq-orgate", + &s->qspi_irq_orgate, TYPE_OR_IRQ); } static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) @@ -709,6 +712,11 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) gic_spi[adma_ch_intr[i]]); } + object_property_set_int(OBJECT(&s->qspi_irq_orgate), + "num-lines", NUM_QSPI_IRQ_LINES, &error_fatal); + qdev_realize(DEVICE(&s->qspi_irq_orgate), NULL, &error_fatal); + qdev_connect_gpio_out(DEVICE(&s->qspi_irq_orgate), 0, gic_spi[QSPI_IRQ]); + if (!object_property_set_link(OBJECT(&s->qspi_dma), "dma", OBJECT(system_memory), errp)) { return; @@ -718,7 +726,8 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) } sysbus_mmio_map(SYS_BUS_DEVICE(&s->qspi_dma), 0, QSPI_DMA_ADDR); - sysbus_connect_irq(SYS_BUS_DEVICE(&s->qspi_dma), 0, gic_spi[QSPI_IRQ]); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->qspi_dma), 0, + qdev_get_gpio_in(DEVICE(&s->qspi_irq_orgate), 0)); if (!object_property_set_link(OBJECT(&s->qspi), "stream-connected-dma", OBJECT(&s->qspi_dma), errp)) { @@ -729,7 +738,8 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) } sysbus_mmio_map(SYS_BUS_DEVICE(&s->qspi), 0, QSPI_ADDR); sysbus_mmio_map(SYS_BUS_DEVICE(&s->qspi), 1, LQSPI_ADDR); - sysbus_connect_irq(SYS_BUS_DEVICE(&s->qspi), 0, gic_spi[QSPI_IRQ]); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->qspi), 0, + qdev_get_gpio_in(DEVICE(&s->qspi_irq_orgate), 1)); for (i = 0; i < XLNX_ZYNQMP_NUM_QSPI_BUS; i++) { g_autofree gchar *bus_name = g_strdup_printf("qspi%d", i); diff --git a/include/hw/arm/xlnx-zynqmp.h b/include/hw/arm/xlnx-zynqmp.h index 062e637fe4..9424f81c37 100644 --- a/include/hw/arm/xlnx-zynqmp.h +++ b/include/hw/arm/xlnx-zynqmp.h @@ -38,6 +38,7 @@ #include "hw/dma/xlnx_csu_dma.h" #include "hw/nvram/xlnx-bbram.h" #include "hw/nvram/xlnx-zynqmp-efuse.h" +#include "hw/or-irq.h" #define TYPE_XLNX_ZYNQMP "xlnx-zynqmp" OBJECT_DECLARE_SIMPLE_TYPE(XlnxZynqMPState, XLNX_ZYNQMP) @@ -122,6 +123,7 @@ struct XlnxZynqMPState { XlnxZDMA gdma[XLNX_ZYNQMP_NUM_GDMA_CH]; XlnxZDMA adma[XLNX_ZYNQMP_NUM_ADMA_CH]; XlnxCSUDMA qspi_dma; + qemu_or_irq qspi_irq_orgate; char *boot_cpu; ARMCPU *boot_cpu_ptr; From bddd892ef1920c9ede00ad2009b3c3b3b0cf7a44 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 27 Jan 2022 15:46:24 +0000 Subject: [PATCH 261/460] target/arm: make psci-conduit settable after realize MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We want to allow the psci-conduit property to be set after realize, because the parts of the code which are best placed to decide if it's OK to enable QEMU's builtin PSCI emulation (the board code and the arm_load_kernel() function are distant from the code which creates and realizes CPUs (typically inside an SoC object's init and realize method) and run afterwards. Since the DEFINE_PROP_* macros don't have support for creating properties which can be changed after realize, change the property to be created with object_property_add_uint32_ptr(), which is what we already use in this function for creating settable-after-realize properties like init-svtor and init-nsvtor. Note that it doesn't conceptually make sense to change the setting of the property after the machine has been completely initialized, beacuse this would mean that the behaviour of the machine when first started would differ from its behaviour when the system is subsequently reset. (It would also require the underlying state to be migrated, which we don't do.) Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Tested-by: Edgar E. Iglesias Tested-by: Cédric Le Goater Message-id: 20220127154639.2090164-2-peter.maydell@linaro.org --- target/arm/cpu.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index cdbc4cdd01..5a9c02a256 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -1317,6 +1317,11 @@ void arm_cpu_post_init(Object *obj) OBJ_PROP_FLAG_READWRITE); } + /* Not DEFINE_PROP_UINT32: we want this to be settable after realize */ + object_property_add_uint32_ptr(obj, "psci-conduit", + &cpu->psci_conduit, + OBJ_PROP_FLAG_READWRITE); + qdev_property_add_static(DEVICE(obj), &arm_cpu_cfgend_property); if (arm_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER)) { @@ -1987,7 +1992,6 @@ static ObjectClass *arm_cpu_class_by_name(const char *cpu_model) } static Property arm_cpu_properties[] = { - DEFINE_PROP_UINT32("psci-conduit", ARMCPU, psci_conduit, 0), DEFINE_PROP_UINT64("midr", ARMCPU, midr, 0), DEFINE_PROP_UINT64("mp-affinity", ARMCPU, mp_affinity, ARM64_AFFINITY_INVALID), From 0c3c25fcda4b7e8a458ab5ca8e5c74be3cc456f1 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 27 Jan 2022 15:46:25 +0000 Subject: [PATCH 262/460] cpu.c: Make start-powered-off settable after realize MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The CPU object's start-powered-off property is currently only settable before the CPU object is realized. For arm machines this is awkward, because we would like to decide whether the CPU should be powered-off based on how we are booting the guest code, which is something done in the machine model code and in common code called by the machine model, which runs much later and in completely different parts of the codebase from the SoC object code that is responsible for creating and realizing the CPU objects. Allow start-powered-off to be set after realize. Since this isn't something that's supported by the DEFINE_PROP_* macros, we have to switch the property definition to use the object_class_property_add_bool() function. Note that it doesn't conceptually make sense to change the setting of the property after the machine has been completely initialized, beacuse this would mean that the behaviour of the machine when first started would differ from its behaviour when the system is subsequently reset. (It would also require the underlying state to be migrated, which we don't do.) Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Tested-by: Edgar E. Iglesias Tested-by: Cédric Le Goater Message-id: 20220127154639.2090164-3-peter.maydell@linaro.org --- cpu.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/cpu.c b/cpu.c index 016bf06a1a..3ea38aea70 100644 --- a/cpu.c +++ b/cpu.c @@ -196,13 +196,33 @@ static Property cpu_common_props[] = { DEFINE_PROP_LINK("memory", CPUState, memory, TYPE_MEMORY_REGION, MemoryRegion *), #endif - DEFINE_PROP_BOOL("start-powered-off", CPUState, start_powered_off, false), DEFINE_PROP_END_OF_LIST(), }; +static bool cpu_get_start_powered_off(Object *obj, Error **errp) +{ + CPUState *cpu = CPU(obj); + return cpu->start_powered_off; +} + +static void cpu_set_start_powered_off(Object *obj, bool value, Error **errp) +{ + CPUState *cpu = CPU(obj); + cpu->start_powered_off = value; +} + void cpu_class_init_props(DeviceClass *dc) { + ObjectClass *oc = OBJECT_CLASS(dc); + device_class_set_props(dc, cpu_common_props); + /* + * We can't use DEFINE_PROP_BOOL in the Property array for this + * property, because we want this to be settable after realize. + */ + object_class_property_add_bool(oc, "start-powered-off", + cpu_get_start_powered_off, + cpu_set_start_powered_off); } void cpu_exec_initfn(CPUState *cpu) From 817e2db8ce276d6d287de81d2526d390369140b6 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 27 Jan 2022 15:46:26 +0000 Subject: [PATCH 263/460] hw/arm/boot: Support setting psci-conduit based on guest EL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently we expect board code to set the psci-conduit property on CPUs and ensure that secondary CPUs are created with the start-powered-off property set to false, if the board wishes to use QEMU's builtin PSCI emulation. This worked OK for the virt board where we first wanted to use it, because the virt board directly creates its CPUs and is in a reasonable position to set those properties. For other boards which model real hardware and use a separate SoC object, however, it is more awkward. Most PSCI-using boards just set the psci-conduit board unconditionally. This was never strictly speaking correct (because you would not be able to run EL3 guest firmware that itself provided the PSCI interface, as the QEMU implementation would overrule it), but mostly worked in practice because for non-PSCI SMC calls QEMU would emulate the SMC instruction as normal (by trapping to guest EL3). However, we would like to make our PSCI emulation follow the part of the SMCC specification that mandates that SMC calls with unknown function identifiers return a failure code, which means that all SMC calls will be handled by the PSCI code and the "emulate as normal" path will no longer be taken. We tried to implement that in commit 9fcd15b9193e81 ("arm: tcg: Adhere to SMCCC 1.3 section 5.2"), but this regressed attempts to run EL3 guest code on the affected boards: * mcimx6ul-evk, mcimx7d-sabre, orangepi, xlnx-zcu102 * for the case only of EL3 code loaded via -kernel (and not via -bios or -pflash), virt and xlnx-versal-virt so for the 7.0 release we reverted it (in commit 4825eaae4fdd56f). This commit provides a mechanism that boards can use to arrange that psci-conduit is set if running guest code at a low enough EL but not if it would be running at the same EL that the conduit implies that the QEMU PSCI implementation is using. (Later commits will convert individual board models to use this mechanism.) We do this by moving the setting of the psci-conduit and start-powered-off properties to arm_load_kernel(). Boards which want to potentially use emulated PSCI must set a psci_conduit field in the arm_boot_info struct to the type of conduit they want to use (SMC or HVC); arm_load_kernel() will then set the CPUs up accordingly if it is not going to start the guest code at the same or higher EL as the fake QEMU firmware would be at. Board/SoC code which uses this mechanism should no longer set the CPU psci-conduit property directly. It should only set the start-powered-off property for secondaries if EL3 guest firmware running bare metal expects that rather than the alternative "all CPUs start executing the firmware at once". Note that when calculating whether we are going to run guest code at EL3, we ignore the setting of arm_boot_info::secure_board_setup, which might cause us to run a stub bit of guest code at EL3 which does some board-specific setup before dropping to EL2 or EL1 to run the guest kernel. This is OK because only one board that enables PSCI sets secure_board_setup (the highbank board), and the stub code it writes will behave the same way whether the one SMC call it makes is handled by "emulate the SMC" or by "PSCI default returns an error code". So we can leave that stub code in place until after we've changed the PSCI default behaviour; at that point we will remove it. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Tested-by: Edgar E. Iglesias Tested-by: Cédric Le Goater Message-id: 20220127154639.2090164-4-peter.maydell@linaro.org --- hw/arm/boot.c | 50 +++++++++++++++++++++++++++++++++++++++++++ include/hw/arm/boot.h | 10 +++++++++ 2 files changed, 60 insertions(+) diff --git a/hw/arm/boot.c b/hw/arm/boot.c index 399f8e837c..327e449f83 100644 --- a/hw/arm/boot.c +++ b/hw/arm/boot.c @@ -1299,6 +1299,8 @@ void arm_load_kernel(ARMCPU *cpu, MachineState *ms, struct arm_boot_info *info) { CPUState *cs; AddressSpace *as = arm_boot_address_space(cpu, info); + int boot_el; + CPUARMState *env = &cpu->env; /* * CPU objects (unlike devices) are not automatically reset on system @@ -1329,6 +1331,54 @@ void arm_load_kernel(ARMCPU *cpu, MachineState *ms, struct arm_boot_info *info) arm_setup_direct_kernel_boot(cpu, info); } + /* + * Disable the PSCI conduit if it is set up to target the same + * or a lower EL than the one we're going to start the guest code in. + * This logic needs to agree with the code in do_cpu_reset() which + * decides whether we're going to boot the guest in the highest + * supported exception level or in a lower one. + */ + + /* Boot into highest supported EL ... */ + if (arm_feature(env, ARM_FEATURE_EL3)) { + boot_el = 3; + } else if (arm_feature(env, ARM_FEATURE_EL2)) { + boot_el = 2; + } else { + boot_el = 1; + } + /* ...except that if we're booting Linux we adjust the EL we boot into */ + if (info->is_linux && !info->secure_boot) { + boot_el = arm_feature(env, ARM_FEATURE_EL2) ? 2 : 1; + } + + if ((info->psci_conduit == QEMU_PSCI_CONDUIT_HVC && boot_el >= 2) || + (info->psci_conduit == QEMU_PSCI_CONDUIT_SMC && boot_el == 3)) { + info->psci_conduit = QEMU_PSCI_CONDUIT_DISABLED; + } + + if (info->psci_conduit != QEMU_PSCI_CONDUIT_DISABLED) { + for (cs = first_cpu; cs; cs = CPU_NEXT(cs)) { + Object *cpuobj = OBJECT(cs); + + object_property_set_int(cpuobj, "psci-conduit", info->psci_conduit, + &error_abort); + /* + * Secondary CPUs start in PSCI powered-down state. Like the + * code in do_cpu_reset(), we assume first_cpu is the primary + * CPU. + */ + if (cs != first_cpu) { + object_property_set_bool(cpuobj, "start-powered-off", true, + &error_abort); + } + } + } + + /* + * arm_load_dtb() may add a PSCI node so it must be called after we have + * decided whether to enable PSCI and set the psci-conduit CPU properties. + */ if (!info->skip_dtb_autoload && have_dtb(info)) { if (arm_load_dtb(info->dtb_start, info, info->dtb_limit, as, ms) < 0) { exit(1); diff --git a/include/hw/arm/boot.h b/include/hw/arm/boot.h index ce2b48b88b..0bcb58babb 100644 --- a/include/hw/arm/boot.h +++ b/include/hw/arm/boot.h @@ -86,6 +86,16 @@ struct arm_boot_info { * the user it should implement this hook. */ void (*modify_dtb)(const struct arm_boot_info *info, void *fdt); + /* + * If a board wants to use the QEMU emulated-firmware PSCI support, + * it should set this to QEMU_PSCI_CONDUIT_HVC or QEMU_PSCI_CONDUIT_SMC + * as appropriate. arm_load_kernel() will set the psci-conduit and + * start-powered-off properties on the CPUs accordingly. + * Note that if the guest image is started at the same exception level + * as the conduit specifies calls should go to (eg guest firmware booted + * to EL3) then PSCI will not be enabled. + */ + int psci_conduit; /* Used internally by arm_boot.c */ int is_linux; hwaddr initrd_start; From ae2474f1189dcbe58b1927b0f955bd4a929df8ba Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 27 Jan 2022 15:46:27 +0000 Subject: [PATCH 264/460] hw/arm: imx: Don't enable PSCI conduit when booting guest in EL3 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change the iMX-SoC based boards to use the new boot.c functionality to allow us to enable psci-conduit only if the guest is being booted in EL1 or EL2, so that if the user runs guest EL3 firmware code our PSCI emulation doesn't get in its way. To do this we stop setting the psci-conduit property on the CPU objects in the SoC code, and instead set the psci_conduit field in the arm_boot_info struct to tell the common boot loader code that we'd like PSCI if the guest is starting at an EL that it makes sense with. This affects the mcimx6ul-evk and mcimx7d-sabre boards. Note that for the mcimx7d board, this means that when running guest code at EL3 there is currently no way to power on the secondary CPUs, because we do not currently have a model of the system reset controller module which should be used to do that for the imx7 SoC, only for the imx6 SoC. (Previously EL3 code which knew it was running on QEMU could use a PSCI call to do this.) This doesn't affect the imx6ul-evk board because it is uniprocessor. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Tested-by: Edgar E. Iglesias Tested-by: Cédric Le Goater Acked-by: Richard Henderson Message-id: 20220127154639.2090164-5-peter.maydell@linaro.org --- hw/arm/fsl-imx6ul.c | 2 -- hw/arm/fsl-imx7.c | 8 ++++---- hw/arm/mcimx6ul-evk.c | 1 + hw/arm/mcimx7d-sabre.c | 1 + 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/hw/arm/fsl-imx6ul.c b/hw/arm/fsl-imx6ul.c index 1d1a708dd9..f189712329 100644 --- a/hw/arm/fsl-imx6ul.c +++ b/hw/arm/fsl-imx6ul.c @@ -166,8 +166,6 @@ static void fsl_imx6ul_realize(DeviceState *dev, Error **errp) return; } - object_property_set_int(OBJECT(&s->cpu), "psci-conduit", - QEMU_PSCI_CONDUIT_SMC, &error_abort); qdev_realize(DEVICE(&s->cpu), NULL, &error_abort); /* diff --git a/hw/arm/fsl-imx7.c b/hw/arm/fsl-imx7.c index 149885f2b8..cc6fdb9373 100644 --- a/hw/arm/fsl-imx7.c +++ b/hw/arm/fsl-imx7.c @@ -159,9 +159,6 @@ static void fsl_imx7_realize(DeviceState *dev, Error **errp) for (i = 0; i < smp_cpus; i++) { o = OBJECT(&s->cpu[i]); - object_property_set_int(o, "psci-conduit", QEMU_PSCI_CONDUIT_SMC, - &error_abort); - /* On uniprocessor, the CBAR is set to 0 */ if (smp_cpus > 1) { object_property_set_int(o, "reset-cbar", FSL_IMX7_A7MPCORE_ADDR, @@ -169,7 +166,10 @@ static void fsl_imx7_realize(DeviceState *dev, Error **errp) } if (i) { - /* Secondary CPUs start in PSCI powered-down state */ + /* + * Secondary CPUs start in powered-down state (and can be + * powered up via the SRC system reset controller) + */ object_property_set_bool(o, "start-powered-off", true, &error_abort); } diff --git a/hw/arm/mcimx6ul-evk.c b/hw/arm/mcimx6ul-evk.c index 28b4886f48..8131518426 100644 --- a/hw/arm/mcimx6ul-evk.c +++ b/hw/arm/mcimx6ul-evk.c @@ -35,6 +35,7 @@ static void mcimx6ul_evk_init(MachineState *machine) .board_id = -1, .ram_size = machine->ram_size, .nb_cpus = machine->smp.cpus, + .psci_conduit = QEMU_PSCI_CONDUIT_SMC, }; s = FSL_IMX6UL(object_new(TYPE_FSL_IMX6UL)); diff --git a/hw/arm/mcimx7d-sabre.c b/hw/arm/mcimx7d-sabre.c index 50a5ecde31..ba84fc2192 100644 --- a/hw/arm/mcimx7d-sabre.c +++ b/hw/arm/mcimx7d-sabre.c @@ -37,6 +37,7 @@ static void mcimx7d_sabre_init(MachineState *machine) .board_id = -1, .ram_size = machine->ram_size, .nb_cpus = machine->smp.cpus, + .psci_conduit = QEMU_PSCI_CONDUIT_SMC, }; s = FSL_IMX7(object_new(TYPE_FSL_IMX7)); From 49865b901466d8f8ad4f16df4bcd076eee268e0f Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 27 Jan 2022 15:46:28 +0000 Subject: [PATCH 265/460] hw/arm: allwinner: Don't enable PSCI conduit when booting guest in EL3 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change the allwinner-h3 based board to use the new boot.c functionality to allow us to enable psci-conduit only if the guest is being booted in EL1 or EL2, so that if the user runs guest EL3 firmware code our PSCI emulation doesn't get in its way. To do this we stop setting the psci-conduit property on the CPU objects in the SoC code, and instead set the psci_conduit field in the arm_boot_info struct to tell the common boot loader code that we'd like PSCI if the guest is starting at an EL that it makes sense with. This affects the orangepi-pc board. This commit leaves the secondary CPUs in the powered-down state if the guest is booting at EL3, which is the same behaviour as before this commit. The secondaries can no longer be started by that EL3 code making a PSCI call but can still be started via the CPU Configuration Module registers (which we model in hw/misc/allwinner-cpucfg.c). Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Niek Linnenbank Tested-by: Cédric Le Goater Tested-by: Niek Linnenbank Message-id: 20220127154639.2090164-6-peter.maydell@linaro.org --- hw/arm/allwinner-h3.c | 9 ++++----- hw/arm/orangepi.c | 1 + 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/hw/arm/allwinner-h3.c b/hw/arm/allwinner-h3.c index f9b7ed1871..318ed4348c 100644 --- a/hw/arm/allwinner-h3.c +++ b/hw/arm/allwinner-h3.c @@ -235,11 +235,10 @@ static void allwinner_h3_realize(DeviceState *dev, Error **errp) /* CPUs */ for (i = 0; i < AW_H3_NUM_CPUS; i++) { - /* Provide Power State Coordination Interface */ - qdev_prop_set_int32(DEVICE(&s->cpus[i]), "psci-conduit", - QEMU_PSCI_CONDUIT_SMC); - - /* Disable secondary CPUs */ + /* + * Disable secondary CPUs. Guest EL3 firmware will start + * them via CPU reset control registers. + */ qdev_prop_set_bit(DEVICE(&s->cpus[i]), "start-powered-off", i > 0); diff --git a/hw/arm/orangepi.c b/hw/arm/orangepi.c index e796382236..68fe918241 100644 --- a/hw/arm/orangepi.c +++ b/hw/arm/orangepi.c @@ -105,6 +105,7 @@ static void orangepi_init(MachineState *machine) } orangepi_binfo.loader_start = h3->memmap[AW_H3_DEV_SDRAM]; orangepi_binfo.ram_size = machine->ram_size; + orangepi_binfo.psci_conduit = QEMU_PSCI_CONDUIT_SMC; arm_load_kernel(ARM_CPU(first_cpu), machine, &orangepi_binfo); } From 50c785f2c70c1e12d01e76cbbd2facc3c8e8d637 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 27 Jan 2022 15:46:29 +0000 Subject: [PATCH 266/460] hw/arm/xlnx-zcu102: Don't enable PSCI conduit when booting guest in EL3 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change the Xilinx ZynqMP-based board xlnx-zcu102 to use the new boot.c functionality to allow us to enable psci-conduit only if the guest is being booted in EL1 or EL2, so that if the user runs guest EL3 firmware code our PSCI emulation doesn't get in its way. To do this we stop setting the psci-conduit property on the CPU objects in the SoC code, and instead set the psci_conduit field in the arm_boot_info struct to tell the common boot loader code that we'd like PSCI if the guest is starting at an EL that it makes sense with. Note that this means that EL3 guest code will have no way to power on secondary cores, because we don't model any kind of power controller that does that on this SoC. Signed-off-by: Peter Maydell Reviewed-by: Edgar E. Iglesias Reviewed-by: Richard Henderson Reviewed-by: Niek Linnenbank Tested-by: Edgar E. Iglesias Tested-by: Cédric Le Goater Tested-by: Niek Linnenbank Acked-by: Richard Henderson Message-id: 20220127154639.2090164-7-peter.maydell@linaro.org --- hw/arm/xlnx-zcu102.c | 1 + hw/arm/xlnx-zynqmp.c | 11 ++++++----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/hw/arm/xlnx-zcu102.c b/hw/arm/xlnx-zcu102.c index 45eb19ab3b..4c84bb932a 100644 --- a/hw/arm/xlnx-zcu102.c +++ b/hw/arm/xlnx-zcu102.c @@ -236,6 +236,7 @@ static void xlnx_zcu102_init(MachineState *machine) s->binfo.ram_size = ram_size; s->binfo.loader_start = 0; s->binfo.modify_dtb = zcu102_modify_dtb; + s->binfo.psci_conduit = QEMU_PSCI_CONDUIT_SMC; arm_load_kernel(s->soc.boot_cpu_ptr, machine, &s->binfo); } diff --git a/hw/arm/xlnx-zynqmp.c b/hw/arm/xlnx-zynqmp.c index 5fbf38c466..6d0e4116db 100644 --- a/hw/arm/xlnx-zynqmp.c +++ b/hw/arm/xlnx-zynqmp.c @@ -216,7 +216,9 @@ static void xlnx_zynqmp_create_rpu(MachineState *ms, XlnxZynqMPState *s, name = object_get_canonical_path_component(OBJECT(&s->rpu_cpu[i])); if (strcmp(name, boot_cpu)) { - /* Secondary CPUs start in PSCI powered-down state */ + /* + * Secondary CPUs start in powered-down state. + */ object_property_set_bool(OBJECT(&s->rpu_cpu[i]), "start-powered-off", true, &error_abort); } else { @@ -438,12 +440,11 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) for (i = 0; i < num_apus; i++) { const char *name; - object_property_set_int(OBJECT(&s->apu_cpu[i]), "psci-conduit", - QEMU_PSCI_CONDUIT_SMC, &error_abort); - name = object_get_canonical_path_component(OBJECT(&s->apu_cpu[i])); if (strcmp(name, boot_cpu)) { - /* Secondary CPUs start in PSCI powered-down state */ + /* + * Secondary CPUs start in powered-down state. + */ object_property_set_bool(OBJECT(&s->apu_cpu[i]), "start-powered-off", true, &error_abort); } else { From 9437a76e1002890fd23da841e1f5bac3f5fa0db7 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 27 Jan 2022 15:46:30 +0000 Subject: [PATCH 267/460] hw/arm/versal: Let boot.c handle PSCI enablement MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of setting the CPU psci-conduit and start-powered-off properties in the xlnx-versal-virt board code, set the arm_boot_info psci_conduit field so that the boot.c code can do it. This will fix a corner case where we were incorrectly enabling PSCI emulation when booting guest code into EL3 because it was an ELF file passed to -kernel. (EL3 guest code started via -bios, -pflash, or the generic loader was already being run with PSCI emulation disabled.) Note that EL3 guest code has no way to turn on the secondary CPUs because there's no emulated power controller, but this was already true for EL3 guest code run via -bios, -pflash, or the generic loader. Signed-off-by: Peter Maydell Reviewed-by: Edgar E. Iglesias Reviewed-by: Richard Henderson Reviewed-by: Niek Linnenbank Tested-by: Edgar E. Iglesias Tested-by: Cédric Le Goater Tested-by: Niek Linnenbank Message-id: 20220127154639.2090164-8-peter.maydell@linaro.org --- hw/arm/xlnx-versal-virt.c | 6 ++++-- hw/arm/xlnx-versal.c | 5 +---- include/hw/arm/xlnx-versal.h | 1 - 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/hw/arm/xlnx-versal-virt.c b/hw/arm/xlnx-versal-virt.c index 3f56ae28ee..ef3231cdbb 100644 --- a/hw/arm/xlnx-versal-virt.c +++ b/hw/arm/xlnx-versal-virt.c @@ -628,6 +628,9 @@ static void versal_virt_init(MachineState *machine) * When loading an OS, we turn on QEMU's PSCI implementation with SMC * as the PSCI conduit. When there's no -kernel, we assume the user * provides EL3 firmware to handle PSCI. + * + * Even if the user provides a kernel filename, arm_load_kernel() + * may suppress PSCI if it's going to boot that guest code at EL3. */ if (machine->kernel_filename) { psci_conduit = QEMU_PSCI_CONDUIT_SMC; @@ -637,8 +640,6 @@ static void versal_virt_init(MachineState *machine) TYPE_XLNX_VERSAL); object_property_set_link(OBJECT(&s->soc), "ddr", OBJECT(machine->ram), &error_abort); - object_property_set_int(OBJECT(&s->soc), "psci-conduit", psci_conduit, - &error_abort); sysbus_realize(SYS_BUS_DEVICE(&s->soc), &error_fatal); fdt_create(s); @@ -679,6 +680,7 @@ static void versal_virt_init(MachineState *machine) s->binfo.loader_start = 0x0; s->binfo.get_dtb = versal_virt_get_dtb; s->binfo.modify_dtb = versal_virt_modify_dtb; + s->binfo.psci_conduit = psci_conduit; if (machine->kernel_filename) { arm_load_kernel(&s->soc.fpd.apu.cpu[0], machine, &s->binfo); } else { diff --git a/hw/arm/xlnx-versal.c b/hw/arm/xlnx-versal.c index ab58bebfd2..2551dfc22d 100644 --- a/hw/arm/xlnx-versal.c +++ b/hw/arm/xlnx-versal.c @@ -40,10 +40,8 @@ static void versal_create_apu_cpus(Versal *s) object_initialize_child(OBJECT(s), "apu-cpu[*]", &s->fpd.apu.cpu[i], XLNX_VERSAL_ACPU_TYPE); obj = OBJECT(&s->fpd.apu.cpu[i]); - object_property_set_int(obj, "psci-conduit", s->cfg.psci_conduit, - &error_abort); if (i) { - /* Secondary CPUs start in PSCI powered-down state */ + /* Secondary CPUs start in powered-down state */ object_property_set_bool(obj, "start-powered-off", true, &error_abort); } @@ -667,7 +665,6 @@ static void versal_init(Object *obj) static Property versal_properties[] = { DEFINE_PROP_LINK("ddr", Versal, cfg.mr_ddr, TYPE_MEMORY_REGION, MemoryRegion *), - DEFINE_PROP_UINT32("psci-conduit", Versal, cfg.psci_conduit, 0), DEFINE_PROP_END_OF_LIST() }; diff --git a/include/hw/arm/xlnx-versal.h b/include/hw/arm/xlnx-versal.h index 1b5ad4de80..0728316ec7 100644 --- a/include/hw/arm/xlnx-versal.h +++ b/include/hw/arm/xlnx-versal.h @@ -103,7 +103,6 @@ struct Versal { struct { MemoryRegion *mr_ddr; - uint32_t psci_conduit; } cfg; }; From 52c235ad7500771f790521d65fb1e19b126a6a32 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 27 Jan 2022 15:46:31 +0000 Subject: [PATCH 268/460] hw/arm/virt: Let boot.c handle PSCI enablement MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of setting the CPU psci-conduit and start-powered-off properties in the virt board code, set the arm_boot_info psci_conduit field so that the boot.c code can do it. This will fix a corner case where we were incorrectly enabling PSCI emulation when booting guest code into EL3 because it was an ELF file passed to -kernel or to the generic loader. (EL3 guest code started via -bios or -pflash was already being run with PSCI emulation disabled.) Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Niek Linnenbank Tested-by: Cédric Le Goater Tested-by: Niek Linnenbank Message-id: 20220127154639.2090164-9-peter.maydell@linaro.org --- hw/arm/virt.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 141350bf21..398145a718 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -2088,17 +2088,6 @@ static void machvirt_init(MachineState *machine) object_property_set_bool(cpuobj, "has_el2", false, NULL); } - if (vms->psci_conduit != QEMU_PSCI_CONDUIT_DISABLED) { - object_property_set_int(cpuobj, "psci-conduit", vms->psci_conduit, - NULL); - - /* Secondary CPUs start in PSCI powered-down state */ - if (n > 0) { - object_property_set_bool(cpuobj, "start-powered-off", true, - NULL); - } - } - if (vmc->kvm_no_adjvtime && object_property_find(cpuobj, "kvm-no-adjvtime")) { object_property_set_bool(cpuobj, "kvm-no-adjvtime", true, NULL); @@ -2246,6 +2235,7 @@ static void machvirt_init(MachineState *machine) vms->bootinfo.get_dtb = machvirt_dtb; vms->bootinfo.skip_dtb_autoload = true; vms->bootinfo.firmware_loaded = firmware_loaded; + vms->bootinfo.psci_conduit = vms->psci_conduit; arm_load_kernel(ARM_CPU(first_cpu), machine, &vms->bootinfo); vms->machine_done.notify = virt_machine_done; From 33284d482c29973a30fee7dfa9728121689fd250 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 27 Jan 2022 15:46:32 +0000 Subject: [PATCH 269/460] hw/arm: highbank: For EL3 guests, don't enable PSCI, start all cores MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change the highbank/midway boards to use the new boot.c functionality to allow us to enable psci-conduit only if the guest is being booted in EL1 or EL2, so that if the user runs guest EL3 firmware code our PSCI emulation doesn't get in its way. To do this we stop setting the psci-conduit and start-powered-off properties on the CPU objects in the board code, and instead set the psci_conduit field in the arm_boot_info struct to tell the common boot loader code that we'd like PSCI if the guest is starting at an EL that it makes sense with (in which case it will set these properties). This means that when running guest code at EL3, all the cores will start execution at once on poweron. This matches the real hardware behaviour. (A brief description of the hardware boot process is in the u-boot documentation for these boards: https://u-boot.readthedocs.io/en/latest/board/highbank/highbank.html#boot-process -- in theory one might run the 'a9boot'/'a15boot' secure monitor code in QEMU, though we probably don't emulate enough for that.) This affects the highbank and midway boards. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Niek Linnenbank Tested-by: Cédric Le Goater Tested-by: Niek Linnenbank Message-id: 20220127154639.2090164-10-peter.maydell@linaro.org --- hw/arm/highbank.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/hw/arm/highbank.c b/hw/arm/highbank.c index 4210894d81..048f8550cb 100644 --- a/hw/arm/highbank.c +++ b/hw/arm/highbank.c @@ -271,12 +271,6 @@ static void calxeda_init(MachineState *machine, enum cxmachines machine_id) object_property_set_int(cpuobj, "psci-conduit", QEMU_PSCI_CONDUIT_SMC, &error_abort); - if (n) { - /* Secondary CPUs start in PSCI powered-down state */ - object_property_set_bool(cpuobj, "start-powered-off", true, - &error_abort); - } - if (object_property_find(cpuobj, "reset-cbar")) { object_property_set_int(cpuobj, "reset-cbar", MPCORE_PERIPHBASE, &error_abort); @@ -397,6 +391,7 @@ static void calxeda_init(MachineState *machine, enum cxmachines machine_id) highbank_binfo.board_setup_addr = BOARD_SETUP_ADDR; highbank_binfo.write_board_setup = hb_write_board_setup; highbank_binfo.secure_board_setup = true; + highbank_binfo.psci_conduit = QEMU_PSCI_CONDUIT_SMC; arm_load_kernel(ARM_CPU(first_cpu), machine, &highbank_binfo); } From 3f37979bf5dae9de7fe23e2d48c53d2ba79afe79 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 7 Feb 2022 16:54:30 +0000 Subject: [PATCH 270/460] arm: tcg: Adhere to SMCCC 1.3 section 5.2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The SMCCC 1.3 spec section 5.2 says The Unknown SMC Function Identifier is a sign-extended value of (-1) that is returned in the R0, W0 or X0 registers. An implementation must return this error code when it receives: * An SMC or HVC call with an unknown Function Identifier * An SMC or HVC call for a removed Function Identifier * An SMC64/HVC64 call from AArch32 state To comply with these statements, let's always return -1 when we encounter an unknown HVC or SMC call. [PMM: This is a reinstatement of commit 9fcd15b9193e819b, previously reverted in commit 4825eaae4fdd56fba0f; we can do this now that we have arranged for all the affected board models to not enable the PSCI emulation if they are running guest code at EL3. This avoids the regressions that caused us to revert the change for 7.0.] Signed-off-by: Alexander Graf Reviewed-by: Richard Henderson Reviewed-by: Niek Linnenbank Tested-by: Edgar E. Iglesias Tested-by: Cédric Le Goater Tested-by: Niek Linnenbank Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target/arm/psci.c | 35 ++++++----------------------------- 1 file changed, 6 insertions(+), 29 deletions(-) diff --git a/target/arm/psci.c b/target/arm/psci.c index 6709e28013..b279c0b9a4 100644 --- a/target/arm/psci.c +++ b/target/arm/psci.c @@ -27,15 +27,13 @@ bool arm_is_psci_call(ARMCPU *cpu, int excp_type) { - /* Return true if the r0/x0 value indicates a PSCI call and - * the exception type matches the configured PSCI conduit. This is - * called before the SMC/HVC instruction is executed, to decide whether - * we should treat it as a PSCI call or with the architecturally + /* + * Return true if the exception type matches the configured PSCI conduit. + * This is called before the SMC/HVC instruction is executed, to decide + * whether we should treat it as a PSCI call or with the architecturally * defined behaviour for an SMC or HVC (which might be UNDEF or trap * to EL2 or to EL3). */ - CPUARMState *env = &cpu->env; - uint64_t param = is_a64(env) ? env->xregs[0] : env->regs[0]; switch (excp_type) { case EXCP_HVC: @@ -52,27 +50,7 @@ bool arm_is_psci_call(ARMCPU *cpu, int excp_type) return false; } - switch (param) { - case QEMU_PSCI_0_2_FN_PSCI_VERSION: - case QEMU_PSCI_0_2_FN_MIGRATE_INFO_TYPE: - case QEMU_PSCI_0_2_FN_AFFINITY_INFO: - case QEMU_PSCI_0_2_FN64_AFFINITY_INFO: - case QEMU_PSCI_0_2_FN_SYSTEM_RESET: - case QEMU_PSCI_0_2_FN_SYSTEM_OFF: - case QEMU_PSCI_0_1_FN_CPU_ON: - case QEMU_PSCI_0_2_FN_CPU_ON: - case QEMU_PSCI_0_2_FN64_CPU_ON: - case QEMU_PSCI_0_1_FN_CPU_OFF: - case QEMU_PSCI_0_2_FN_CPU_OFF: - case QEMU_PSCI_0_1_FN_CPU_SUSPEND: - case QEMU_PSCI_0_2_FN_CPU_SUSPEND: - case QEMU_PSCI_0_2_FN64_CPU_SUSPEND: - case QEMU_PSCI_0_1_FN_MIGRATE: - case QEMU_PSCI_0_2_FN_MIGRATE: - return true; - default: - return false; - } + return true; } void arm_handle_psci_call(ARMCPU *cpu) @@ -194,10 +172,9 @@ void arm_handle_psci_call(ARMCPU *cpu) break; case QEMU_PSCI_0_1_FN_MIGRATE: case QEMU_PSCI_0_2_FN_MIGRATE: + default: ret = QEMU_PSCI_RET_NOT_SUPPORTED; break; - default: - g_assert_not_reached(); } err: From 61b82973e746ff750fbbafe10fa6e3c416b01321 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 27 Jan 2022 15:46:34 +0000 Subject: [PATCH 271/460] hw/arm/highbank: Drop use of secure_board_setup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Guest code on highbank may make non-PSCI SMC calls in order to enable/disable the L2x0 cache controller (see the Linux kernel's arch/arm/mach-highbank/highbank.c highbank_l2c310_write_sec() function). The ABI for this is documented in kernel commit 8e56130dcb as being borrowed from the OMAP44xx ROM. The OMAP44xx TRM documents this function ID as having no return value and potentially trashing all guest registers except SP and PC. For QEMU's purposes (where our L2x0 model is a stub and enabling or disabling it doesn't affect the guest behaviour) a simple "do nothing" SMC is fine. We currently implement this NOP behaviour using a little bit of Secure code we run before jumping to the guest kernel, which is written by arm_write_secure_board_setup_dummy_smc(). The code sets up a set of Secure vectors where the SMC entry point returns without doing anything. Now that the PSCI SMC emulation handles all SMC calls (setting r0 to an error code if the input r0 function identifier is not recognized), we can use that default behaviour as sufficient for the highbank cache controller call. (Because the guest code assumes r0 has no interesting value on exit it doesn't matter that we set it to the error code). We can therefore delete the highbank board code that sets secure_board_setup to true and writes the secure-code bootstub. (Note that because the OMAP44xx ABI puts function-identifiers in r12 and PSCI uses r0, we only avoid a clash because Linux's code happens to put the function-identifier in both registers. But this is true also when the kernel is running on real firmware that implements both ABIs as far as I can see.) This change fixes in passing booting on the 'midway' board model, which has been completely broken since we added support for Hyp mode to the Cortex-A15 CPU. When we did that boot.c was made to start running the guest code in Hyp mode; this includes the board_setup hook, which instantly UNDEFs because the NSACR is not accessible from Hyp. (Put another way, we never made the secure_board_setup hook support cope with Hyp mode.) Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Niek Linnenbank Tested-by: Cédric Le Goater Tested-by: Niek Linnenbank Message-id: 20220127154639.2090164-12-peter.maydell@linaro.org --- hw/arm/highbank.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/hw/arm/highbank.c b/hw/arm/highbank.c index 048f8550cb..a21afd178d 100644 --- a/hw/arm/highbank.c +++ b/hw/arm/highbank.c @@ -48,12 +48,6 @@ /* Board init. */ -static void hb_write_board_setup(ARMCPU *cpu, - const struct arm_boot_info *info) -{ - arm_write_secure_board_setup_dummy_smc(cpu, info, MVBAR_ADDR); -} - static void hb_write_secondary(ARMCPU *cpu, const struct arm_boot_info *info) { int n; @@ -389,8 +383,6 @@ static void calxeda_init(MachineState *machine, enum cxmachines machine_id) highbank_binfo.write_secondary_boot = hb_write_secondary; highbank_binfo.secondary_cpu_reset_hook = hb_reset_secondary; highbank_binfo.board_setup_addr = BOARD_SETUP_ADDR; - highbank_binfo.write_board_setup = hb_write_board_setup; - highbank_binfo.secure_board_setup = true; highbank_binfo.psci_conduit = QEMU_PSCI_CONDUIT_SMC; arm_load_kernel(ARM_CPU(first_cpu), machine, &highbank_binfo); From dc888dd43bea83b1ccc3d0554d5044179554a5f1 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 27 Jan 2022 15:46:35 +0000 Subject: [PATCH 272/460] hw/arm/boot: Prevent setting both psci_conduit and secure_board_setup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that we have dealt with the one special case (highbank) that needed to set both psci_conduit and secure_board_setup, we don't need to allow that combination any more. It doesn't make sense in general, so use an assertion to ensure we don't add new boards that do it by accident without thinking through the consequences. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Niek Linnenbank Tested-by: Cédric Le Goater Tested-by: Niek Linnenbank Message-id: 20220127154639.2090164-13-peter.maydell@linaro.org --- hw/arm/boot.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/hw/arm/boot.c b/hw/arm/boot.c index 327e449f83..0424c17830 100644 --- a/hw/arm/boot.c +++ b/hw/arm/boot.c @@ -1339,6 +1339,16 @@ void arm_load_kernel(ARMCPU *cpu, MachineState *ms, struct arm_boot_info *info) * supported exception level or in a lower one. */ + /* + * If PSCI is enabled, then SMC calls all go to the PSCI handler and + * are never emulated to trap into guest code. It therefore does not + * make sense for the board to have a setup code fragment that runs + * in Secure, because this will probably need to itself issue an SMC of some + * kind as part of its operation. + */ + assert(info->psci_conduit == QEMU_PSCI_CONDUIT_DISABLED || + !info->secure_board_setup); + /* Boot into highest supported EL ... */ if (arm_feature(env, ARM_FEATURE_EL3)) { boot_el = 3; From d4a29ed6db32ae2f21561e87e27936782e0b8a44 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 27 Jan 2022 15:46:36 +0000 Subject: [PATCH 273/460] hw/arm/boot: Don't write secondary boot stub if using PSCI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If we're using PSCI emulation to start secondary CPUs, there is no point in writing the "secondary boot" stub code, because it will never be used -- secondary CPUs start powered-off, and when powered on are set to begin execution at the address specified by the guest's power-on PSCI call, not at the stub. Move the call to the hook that writes the secondary boot stub code so that we can do it only if we're starting a Linux kernel and not using PSCI. (None of the users of the hook care about the ordering of its call relative to anything else: they only use it to write a rom blob to guest memory.) Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Niek Linnenbank Tested-by: Cédric Le Goater Tested-by: Niek Linnenbank Message-id: 20220127154639.2090164-14-peter.maydell@linaro.org --- hw/arm/boot.c | 35 ++++++++++++++++++++++++----------- include/hw/arm/boot.h | 3 +++ 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/hw/arm/boot.c b/hw/arm/boot.c index 0424c17830..184628ce56 100644 --- a/hw/arm/boot.c +++ b/hw/arm/boot.c @@ -804,7 +804,7 @@ static void do_cpu_reset(void *opaque) set_kernel_args(info, as); } } - } else { + } else if (info->secondary_cpu_reset_hook) { info->secondary_cpu_reset_hook(cpu, info); } } @@ -1030,13 +1030,6 @@ static void arm_setup_direct_kernel_boot(ARMCPU *cpu, elf_machine = EM_ARM; } - if (!info->secondary_cpu_reset_hook) { - info->secondary_cpu_reset_hook = default_reset_secondary; - } - if (!info->write_secondary_boot) { - info->write_secondary_boot = default_write_secondary; - } - if (info->nb_cpus == 0) info->nb_cpus = 1; @@ -1216,9 +1209,6 @@ static void arm_setup_direct_kernel_boot(ARMCPU *cpu, write_bootloader("bootloader", info->loader_start, primary_loader, fixupcontext, as); - if (info->nb_cpus > 1) { - info->write_secondary_boot(cpu, info); - } if (info->write_board_setup) { info->write_board_setup(cpu, info); } @@ -1385,6 +1375,29 @@ void arm_load_kernel(ARMCPU *cpu, MachineState *ms, struct arm_boot_info *info) } } + if (info->psci_conduit == QEMU_PSCI_CONDUIT_DISABLED && + info->is_linux && info->nb_cpus > 1) { + /* + * We're booting Linux but not using PSCI, so for SMP we need + * to write a custom secondary CPU boot loader stub, and arrange + * for the secondary CPU reset to make the accompanying initialization. + */ + if (!info->secondary_cpu_reset_hook) { + info->secondary_cpu_reset_hook = default_reset_secondary; + } + if (!info->write_secondary_boot) { + info->write_secondary_boot = default_write_secondary; + } + info->write_secondary_boot(cpu, info); + } else { + /* + * No secondary boot stub; don't use the reset hook that would + * have set the CPU up to call it + */ + info->write_secondary_boot = NULL; + info->secondary_cpu_reset_hook = NULL; + } + /* * arm_load_dtb() may add a PSCI node so it must be called after we have * decided whether to enable PSCI and set the psci-conduit CPU properties. diff --git a/include/hw/arm/boot.h b/include/hw/arm/boot.h index 0bcb58babb..0cfc1c95c4 100644 --- a/include/hw/arm/boot.h +++ b/include/hw/arm/boot.h @@ -70,6 +70,9 @@ struct arm_boot_info { * boot loader/boot ROM code, and secondary_cpu_reset_hook() should * perform any necessary CPU reset handling and set the PC for the * secondary CPUs to point at this boot blob. + * + * These hooks won't be called if secondary CPUs are booting via + * emulated PSCI (see psci_conduit below). */ void (*write_secondary_boot)(ARMCPU *cpu, const struct arm_boot_info *info); From 45dd668f2382475c71528b00465aaaf791cc4369 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 27 Jan 2022 15:46:37 +0000 Subject: [PATCH 274/460] hw/arm/highbank: Drop unused secondary boot stub code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The highbank and midway board code includes boot-stub code for handling secondary CPU boot which keeps the secondaries in a pen until the primary writes to a known location with the address they should jump to. This code is never used, because the boards enable QEMU's PSCI emulation, so secondary CPUs are kept powered off until the PSCI call which turns them on, and then start execution from the address given by the guest in that PSCI call. Delete the unreachable code. (The code was wrong for midway in any case -- on the Cortex-A15 the GIC CPU interface registers are at a different offset from PERIPHBASE compared to the Cortex-A9, and the code baked-in the offsets for highbank's A9.) Note that this commit implicitly depends on the preceding "Don't write secondary boot stub if using PSCI" commit -- the default secondary-boot stub code overlaps with one of the highbank-specific bootcode rom blobs, so we must suppress the secondary-boot stub code entirely, not merely replace the highbank-specific version with the default. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Niek Linnenbank Tested-by: Cédric Le Goater Tested-by: Niek Linnenbank Message-id: 20220127154639.2090164-15-peter.maydell@linaro.org --- hw/arm/highbank.c | 56 ----------------------------------------------- 1 file changed, 56 deletions(-) diff --git a/hw/arm/highbank.c b/hw/arm/highbank.c index a21afd178d..da681b1570 100644 --- a/hw/arm/highbank.c +++ b/hw/arm/highbank.c @@ -48,60 +48,6 @@ /* Board init. */ -static void hb_write_secondary(ARMCPU *cpu, const struct arm_boot_info *info) -{ - int n; - uint32_t smpboot[] = { - 0xee100fb0, /* mrc p15, 0, r0, c0, c0, 5 - read current core id */ - 0xe210000f, /* ands r0, r0, #0x0f */ - 0xe3a03040, /* mov r3, #0x40 - jump address is 0x40 + 0x10 * core id */ - 0xe0830200, /* add r0, r3, r0, lsl #4 */ - 0xe59f2024, /* ldr r2, privbase */ - 0xe3a01001, /* mov r1, #1 */ - 0xe5821100, /* str r1, [r2, #256] - set GICC_CTLR.Enable */ - 0xe3a010ff, /* mov r1, #0xff */ - 0xe5821104, /* str r1, [r2, #260] - set GICC_PMR.Priority to 0xff */ - 0xf57ff04f, /* dsb */ - 0xe320f003, /* wfi */ - 0xe5901000, /* ldr r1, [r0] */ - 0xe1110001, /* tst r1, r1 */ - 0x0afffffb, /* beq */ - 0xe12fff11, /* bx r1 */ - MPCORE_PERIPHBASE /* privbase: MPCore peripheral base address. */ - }; - for (n = 0; n < ARRAY_SIZE(smpboot); n++) { - smpboot[n] = tswap32(smpboot[n]); - } - rom_add_blob_fixed_as("smpboot", smpboot, sizeof(smpboot), SMP_BOOT_ADDR, - arm_boot_address_space(cpu, info)); -} - -static void hb_reset_secondary(ARMCPU *cpu, const struct arm_boot_info *info) -{ - CPUARMState *env = &cpu->env; - - switch (info->nb_cpus) { - case 4: - address_space_stl_notdirty(&address_space_memory, - SMP_BOOT_REG + 0x30, 0, - MEMTXATTRS_UNSPECIFIED, NULL); - /* fallthrough */ - case 3: - address_space_stl_notdirty(&address_space_memory, - SMP_BOOT_REG + 0x20, 0, - MEMTXATTRS_UNSPECIFIED, NULL); - /* fallthrough */ - case 2: - address_space_stl_notdirty(&address_space_memory, - SMP_BOOT_REG + 0x10, 0, - MEMTXATTRS_UNSPECIFIED, NULL); - env->regs[15] = SMP_BOOT_ADDR; - break; - default: - break; - } -} - #define NUM_REGS 0x200 static void hb_regs_write(void *opaque, hwaddr offset, uint64_t value, unsigned size) @@ -380,8 +326,6 @@ static void calxeda_init(MachineState *machine, enum cxmachines machine_id) highbank_binfo.board_id = -1; highbank_binfo.nb_cpus = smp_cpus; highbank_binfo.loader_start = 0; - highbank_binfo.write_secondary_boot = hb_write_secondary; - highbank_binfo.secondary_cpu_reset_hook = hb_reset_secondary; highbank_binfo.board_setup_addr = BOARD_SETUP_ADDR; highbank_binfo.psci_conduit = QEMU_PSCI_CONDUIT_SMC; From d6dc926e6e81dbb7e28d0842f7e78f99b80ce650 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 27 Jan 2022 15:46:38 +0000 Subject: [PATCH 275/460] hw/arm/boot: Drop nb_cpus field from arm_boot_info MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We use the arm_boot_info::nb_cpus field in only one place, and that place can easily get the number of CPUs locally rather than relying on the board code to have set the field correctly. (At least one board, xlnx-versal-virt, does not set the field despite having more than one CPU.) Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Niek Linnenbank Tested-by: Cédric Le Goater Tested-by: Niek Linnenbank Message-id: 20220127154639.2090164-16-peter.maydell@linaro.org --- hw/arm/aspeed.c | 1 - hw/arm/boot.c | 7 +++---- hw/arm/exynos4_boards.c | 1 - hw/arm/highbank.c | 1 - hw/arm/imx25_pdk.c | 3 +-- hw/arm/kzm.c | 1 - hw/arm/mcimx6ul-evk.c | 1 - hw/arm/mcimx7d-sabre.c | 1 - hw/arm/npcm7xx.c | 3 --- hw/arm/orangepi.c | 4 +--- hw/arm/raspi.c | 1 - hw/arm/realview.c | 1 - hw/arm/sabrelite.c | 1 - hw/arm/sbsa-ref.c | 1 - hw/arm/vexpress.c | 1 - hw/arm/virt.c | 1 - hw/arm/xilinx_zynq.c | 1 - include/hw/arm/boot.h | 1 - 18 files changed, 5 insertions(+), 26 deletions(-) diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c index cf20ae0db5..d911dc904f 100644 --- a/hw/arm/aspeed.c +++ b/hw/arm/aspeed.c @@ -431,7 +431,6 @@ static void aspeed_machine_init(MachineState *machine) aspeed_board_binfo.ram_size = machine->ram_size; aspeed_board_binfo.loader_start = sc->memmap[ASPEED_DEV_SDRAM]; - aspeed_board_binfo.nb_cpus = sc->num_cpus; if (amc->i2c_init) { amc->i2c_init(bmc); diff --git a/hw/arm/boot.c b/hw/arm/boot.c index 184628ce56..b46f1fe889 100644 --- a/hw/arm/boot.c +++ b/hw/arm/boot.c @@ -1030,9 +1030,6 @@ static void arm_setup_direct_kernel_boot(ARMCPU *cpu, elf_machine = EM_ARM; } - if (info->nb_cpus == 0) - info->nb_cpus = 1; - /* Assume that raw images are linux kernels, and ELF images are not. */ kernel_size = arm_load_elf(info, &elf_entry, &image_low_addr, &image_high_addr, elf_machine, as); @@ -1291,6 +1288,7 @@ void arm_load_kernel(ARMCPU *cpu, MachineState *ms, struct arm_boot_info *info) AddressSpace *as = arm_boot_address_space(cpu, info); int boot_el; CPUARMState *env = &cpu->env; + int nb_cpus = 0; /* * CPU objects (unlike devices) are not automatically reset on system @@ -1300,6 +1298,7 @@ void arm_load_kernel(ARMCPU *cpu, MachineState *ms, struct arm_boot_info *info) */ for (cs = first_cpu; cs; cs = CPU_NEXT(cs)) { qemu_register_reset(do_cpu_reset, ARM_CPU(cs)); + nb_cpus++; } /* @@ -1376,7 +1375,7 @@ void arm_load_kernel(ARMCPU *cpu, MachineState *ms, struct arm_boot_info *info) } if (info->psci_conduit == QEMU_PSCI_CONDUIT_DISABLED && - info->is_linux && info->nb_cpus > 1) { + info->is_linux && nb_cpus > 1) { /* * We're booting Linux but not using PSCI, so for SMP we need * to write a custom secondary CPU boot loader stub, and arrange diff --git a/hw/arm/exynos4_boards.c b/hw/arm/exynos4_boards.c index 35dd9875da..ef5bcbc212 100644 --- a/hw/arm/exynos4_boards.c +++ b/hw/arm/exynos4_boards.c @@ -67,7 +67,6 @@ static unsigned long exynos4_board_ram_size[EXYNOS4_NUM_OF_BOARDS] = { static struct arm_boot_info exynos4_board_binfo = { .loader_start = EXYNOS4210_BASE_BOOT_ADDR, .smp_loader_start = EXYNOS4210_SMP_BOOT_ADDR, - .nb_cpus = EXYNOS4210_NCPUS, .write_secondary_boot = exynos4210_write_secondary, }; diff --git a/hw/arm/highbank.c b/hw/arm/highbank.c index da681b1570..f12aacea6b 100644 --- a/hw/arm/highbank.c +++ b/hw/arm/highbank.c @@ -324,7 +324,6 @@ static void calxeda_init(MachineState *machine, enum cxmachines machine_id) * clear that the value is meaningless. */ highbank_binfo.board_id = -1; - highbank_binfo.nb_cpus = smp_cpus; highbank_binfo.loader_start = 0; highbank_binfo.board_setup_addr = BOARD_SETUP_ADDR; highbank_binfo.psci_conduit = QEMU_PSCI_CONDUIT_SMC; diff --git a/hw/arm/imx25_pdk.c b/hw/arm/imx25_pdk.c index 6dff000163..b4f7f4e8a7 100644 --- a/hw/arm/imx25_pdk.c +++ b/hw/arm/imx25_pdk.c @@ -114,8 +114,7 @@ static void imx25_pdk_init(MachineState *machine) imx25_pdk_binfo.ram_size = machine->ram_size; imx25_pdk_binfo.loader_start = FSL_IMX25_SDRAM0_ADDR; - imx25_pdk_binfo.board_id = 1771, - imx25_pdk_binfo.nb_cpus = 1; + imx25_pdk_binfo.board_id = 1771; for (i = 0; i < FSL_IMX25_NUM_ESDHCS; i++) { BusState *bus; diff --git a/hw/arm/kzm.c b/hw/arm/kzm.c index 39559c44c2..b1b281c9ac 100644 --- a/hw/arm/kzm.c +++ b/hw/arm/kzm.c @@ -124,7 +124,6 @@ static void kzm_init(MachineState *machine) } kzm_binfo.ram_size = machine->ram_size; - kzm_binfo.nb_cpus = 1; if (!qtest_enabled()) { arm_load_kernel(&s->soc.cpu, machine, &kzm_binfo); diff --git a/hw/arm/mcimx6ul-evk.c b/hw/arm/mcimx6ul-evk.c index 8131518426..d83c3c380e 100644 --- a/hw/arm/mcimx6ul-evk.c +++ b/hw/arm/mcimx6ul-evk.c @@ -34,7 +34,6 @@ static void mcimx6ul_evk_init(MachineState *machine) .loader_start = FSL_IMX6UL_MMDC_ADDR, .board_id = -1, .ram_size = machine->ram_size, - .nb_cpus = machine->smp.cpus, .psci_conduit = QEMU_PSCI_CONDUIT_SMC, }; diff --git a/hw/arm/mcimx7d-sabre.c b/hw/arm/mcimx7d-sabre.c index ba84fc2192..6182b15f19 100644 --- a/hw/arm/mcimx7d-sabre.c +++ b/hw/arm/mcimx7d-sabre.c @@ -36,7 +36,6 @@ static void mcimx7d_sabre_init(MachineState *machine) .loader_start = FSL_IMX7_MMDC_ADDR, .board_id = -1, .ram_size = machine->ram_size, - .nb_cpus = machine->smp.cpus, .psci_conduit = QEMU_PSCI_CONDUIT_SMC, }; diff --git a/hw/arm/npcm7xx.c b/hw/arm/npcm7xx.c index 878c2208e0..d85cc02765 100644 --- a/hw/arm/npcm7xx.c +++ b/hw/arm/npcm7xx.c @@ -355,10 +355,7 @@ static struct arm_boot_info npcm7xx_binfo = { void npcm7xx_load_kernel(MachineState *machine, NPCM7xxState *soc) { - NPCM7xxClass *sc = NPCM7XX_GET_CLASS(soc); - npcm7xx_binfo.ram_size = machine->ram_size; - npcm7xx_binfo.nb_cpus = sc->num_cpus; arm_load_kernel(&soc->cpu[0], machine, &npcm7xx_binfo); } diff --git a/hw/arm/orangepi.c b/hw/arm/orangepi.c index 68fe918241..3ace474870 100644 --- a/hw/arm/orangepi.c +++ b/hw/arm/orangepi.c @@ -25,9 +25,7 @@ #include "hw/qdev-properties.h" #include "hw/arm/allwinner-h3.h" -static struct arm_boot_info orangepi_binfo = { - .nb_cpus = AW_H3_NUM_CPUS, -}; +static struct arm_boot_info orangepi_binfo; static void orangepi_init(MachineState *machine) { diff --git a/hw/arm/raspi.c b/hw/arm/raspi.c index b4dd6c1e99..92d068d1f9 100644 --- a/hw/arm/raspi.c +++ b/hw/arm/raspi.c @@ -204,7 +204,6 @@ static void setup_boot(MachineState *machine, RaspiProcessorId processor_id, s->binfo.board_id = MACH_TYPE_BCM2708; s->binfo.ram_size = ram_size; - s->binfo.nb_cpus = machine->smp.cpus; if (processor_id <= PROCESSOR_ID_BCM2836) { /* diff --git a/hw/arm/realview.c b/hw/arm/realview.c index ddc70b54a5..7b424e94a5 100644 --- a/hw/arm/realview.c +++ b/hw/arm/realview.c @@ -363,7 +363,6 @@ static void realview_init(MachineState *machine, memory_region_add_subregion(sysmem, SMP_BOOT_ADDR, ram_hack); realview_binfo.ram_size = ram_size; - realview_binfo.nb_cpus = smp_cpus; realview_binfo.board_id = realview_board_id[board_type]; realview_binfo.loader_start = (board_type == BOARD_PB_A8 ? 0x70000000 : 0); arm_load_kernel(ARM_CPU(first_cpu), machine, &realview_binfo); diff --git a/hw/arm/sabrelite.c b/hw/arm/sabrelite.c index cce49aa25c..41191245b8 100644 --- a/hw/arm/sabrelite.c +++ b/hw/arm/sabrelite.c @@ -93,7 +93,6 @@ static void sabrelite_init(MachineState *machine) } sabrelite_binfo.ram_size = machine->ram_size; - sabrelite_binfo.nb_cpus = machine->smp.cpus; sabrelite_binfo.secure_boot = true; sabrelite_binfo.write_secondary_boot = sabrelite_write_secondary; sabrelite_binfo.secondary_cpu_reset_hook = sabrelite_reset_secondary; diff --git a/hw/arm/sbsa-ref.c b/hw/arm/sbsa-ref.c index dd944553f7..2387401963 100644 --- a/hw/arm/sbsa-ref.c +++ b/hw/arm/sbsa-ref.c @@ -776,7 +776,6 @@ static void sbsa_ref_init(MachineState *machine) create_secure_ec(secure_sysmem); sms->bootinfo.ram_size = machine->ram_size; - sms->bootinfo.nb_cpus = smp_cpus; sms->bootinfo.board_id = -1; sms->bootinfo.loader_start = sbsa_ref_memmap[SBSA_MEM].base; sms->bootinfo.get_dtb = sbsa_ref_dtb; diff --git a/hw/arm/vexpress.c b/hw/arm/vexpress.c index 3e99b7918a..e1d1983ae6 100644 --- a/hw/arm/vexpress.c +++ b/hw/arm/vexpress.c @@ -708,7 +708,6 @@ static void vexpress_common_init(MachineState *machine) } daughterboard->bootinfo.ram_size = machine->ram_size; - daughterboard->bootinfo.nb_cpus = machine->smp.cpus; daughterboard->bootinfo.board_id = VEXPRESS_BOARD_ID; daughterboard->bootinfo.loader_start = daughterboard->loader_start; daughterboard->bootinfo.smp_loader_start = map[VE_SRAM]; diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 398145a718..46bf7ceddf 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -2229,7 +2229,6 @@ static void machvirt_init(MachineState *machine) } vms->bootinfo.ram_size = machine->ram_size; - vms->bootinfo.nb_cpus = smp_cpus; vms->bootinfo.board_id = -1; vms->bootinfo.loader_start = vms->memmap[VIRT_MEM].base; vms->bootinfo.get_dtb = machvirt_dtb; diff --git a/hw/arm/xilinx_zynq.c b/hw/arm/xilinx_zynq.c index 50e7268396..3190cc0b8d 100644 --- a/hw/arm/xilinx_zynq.c +++ b/hw/arm/xilinx_zynq.c @@ -343,7 +343,6 @@ static void zynq_init(MachineState *machine) sysbus_mmio_map(busdev, 0, 0xF8007000); zynq_binfo.ram_size = machine->ram_size; - zynq_binfo.nb_cpus = 1; zynq_binfo.board_id = 0xd32; zynq_binfo.loader_start = 0; zynq_binfo.board_setup_addr = BOARD_SETUP_ADDR; diff --git a/include/hw/arm/boot.h b/include/hw/arm/boot.h index 0cfc1c95c4..c7ebae156e 100644 --- a/include/hw/arm/boot.h +++ b/include/hw/arm/boot.h @@ -56,7 +56,6 @@ struct arm_boot_info { hwaddr smp_loader_start; hwaddr smp_bootreg_addr; hwaddr gic_cpu_if_addr; - int nb_cpus; int board_id; /* ARM machines that support the ARM Security Extensions use this field to * control whether Linux is booted as secure(true) or non-secure(false). From e4b0bb80713a8ae530fd868ca84543f1b8ecb290 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 27 Jan 2022 15:46:39 +0000 Subject: [PATCH 276/460] hw/arm/boot: Drop existing dtb /psci node rather than retaining it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If we're using PSCI emulation, we add a /psci node to the device tree we pass to the guest. At the moment, if the dtb already has a /psci node in it, we retain it, rather than replacing it. (This behaviour was added in commit c39770cd637765 in 2018.) This is a problem if the existing node doesn't match our PSCI emulation. In particular, it might specify the wrong method (HVC vs SMC), or wrong function IDs for cpu_suspend/cpu_off/etc, in which case the guest will not get the behaviour it wants when it makes PSCI calls. An example of this is trying to boot the highbank or midway board models using the device tree supplied in the kernel sources: this device tree includes a /psci node that specifies function IDs that don't match the (PSCI 0.2 compliant) IDs that QEMU uses. The dtb cpu_suspend function ID happens to match the PSCI 0.2 cpu_off ID, so the guest hangs after booting when the kernel tries to idle the CPU and instead it gets turned off. Instead of retaining an existing /psci node, delete it entirely and replace it with a node whose properties match QEMU's PSCI emulation behaviour. This matches the way we handle /memory nodes, where we also delete any existing nodes and write in ones that match the way QEMU is going to behave. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Niek Linnenbank Tested-by: Edgar E. Iglesias Tested-by: Cédric Le Goater Tested-by: Niek Linnenbank Message-id: 20220127154639.2090164-17-peter.maydell@linaro.org --- hw/arm/boot.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/hw/arm/boot.c b/hw/arm/boot.c index b46f1fe889..b1e95978f2 100644 --- a/hw/arm/boot.c +++ b/hw/arm/boot.c @@ -478,12 +478,13 @@ static void fdt_add_psci_node(void *fdt) } /* - * If /psci node is present in provided DTB, assume that no fixup - * is necessary and all PSCI configuration should be taken as-is + * A pre-existing /psci node might specify function ID values + * that don't match QEMU's PSCI implementation. Delete the whole + * node and put our own in instead. */ rc = fdt_path_offset(fdt, "/psci"); if (rc >= 0) { - return; + qemu_fdt_nop_node(fdt, "/psci"); } qemu_fdt_add_subnode(fdt, "/psci"); From 40874a383dd9b4bca0f09b07641487919645d8c4 Mon Sep 17 00:00:00 2001 From: "Edgar E. Iglesias" Date: Sun, 30 Jan 2022 12:03:13 +0100 Subject: [PATCH 277/460] hw/arm: versal-virt: Always call arm_load_kernel() Always call arm_load_kernel() regardless of kernel_filename being set. This is needed because arm_load_kernel() sets up reset for the CPUs. Fixes: 6f16da53ff (hw/arm: versal: Add a virtual Xilinx Versal board) Reported-by: Peter Maydell Signed-off-by: Edgar E. Iglesias Message-id: 20220130110313.4045351-2-edgar.iglesias@gmail.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/xlnx-versal-virt.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/hw/arm/xlnx-versal-virt.c b/hw/arm/xlnx-versal-virt.c index ef3231cdbb..7c7baff8b7 100644 --- a/hw/arm/xlnx-versal-virt.c +++ b/hw/arm/xlnx-versal-virt.c @@ -681,20 +681,13 @@ static void versal_virt_init(MachineState *machine) s->binfo.get_dtb = versal_virt_get_dtb; s->binfo.modify_dtb = versal_virt_modify_dtb; s->binfo.psci_conduit = psci_conduit; - if (machine->kernel_filename) { - arm_load_kernel(&s->soc.fpd.apu.cpu[0], machine, &s->binfo); - } else { - AddressSpace *as = arm_boot_address_space(&s->soc.fpd.apu.cpu[0], - &s->binfo); + if (!machine->kernel_filename) { /* Some boot-loaders (e.g u-boot) don't like blobs at address 0 (NULL). * Offset things by 4K. */ s->binfo.loader_start = 0x1000; s->binfo.dtb_limit = 0x1000000; - if (arm_load_dtb(s->binfo.loader_start, - &s->binfo, s->binfo.dtb_limit, as, machine) < 0) { - exit(EXIT_FAILURE); - } } + arm_load_kernel(&s->soc.fpd.apu.cpu[0], machine, &s->binfo); for (i = 0; i < XLNX_VERSAL_NUM_OSPI_FLASH; i++) { BusState *spi_bus; From c737d868047f6ae91325adcd3a40f509753a1d85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Wed, 2 Feb 2022 12:23:53 +0000 Subject: [PATCH 278/460] arm: force flag recalculation when messing with DAIF MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The recently introduced debug tests in kvm-unit-tests exposed an error in our handling of singlestep cause by stale hflags. This is caught by --enable-debug-tcg when running the tests. Signed-off-by: Alex Bennée Reported-by: Andrew Jones Tested-by: Andrew Jones Reviewed-by: Richard Henderson Message-id: 20220202122353.457084-1-alex.bennee@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper-a64.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/target/arm/helper-a64.c b/target/arm/helper-a64.c index d6a6fd73d9..7cf953b1e6 100644 --- a/target/arm/helper-a64.c +++ b/target/arm/helper-a64.c @@ -83,12 +83,14 @@ void HELPER(msr_i_daifset)(CPUARMState *env, uint32_t imm) { daif_check(env, 0x1e, imm, GETPC()); env->daif |= (imm << 6) & PSTATE_DAIF; + arm_rebuild_hflags(env); } void HELPER(msr_i_daifclear)(CPUARMState *env, uint32_t imm) { daif_check(env, 0x1f, imm, GETPC()); env->daif &= ~((imm << 6) & PSTATE_DAIF); + arm_rebuild_hflags(env); } /* Convert a softfloat float_relation_ (as returned by From 77cd997161cc853c758b68eebb52827d56bc020e Mon Sep 17 00:00:00 2001 From: Richard Petri Date: Tue, 1 Feb 2022 20:26:51 +0100 Subject: [PATCH 279/460] hw/timer/armv7m_systick: Update clock source before enabling timer Starting the SysTick timer and changing the clock source a the same time will result in an error, if the previous clock period was zero. For exmaple, on the mps2-tz platforms, no refclk is present. Right after reset, the configured ptimer period is zero, and trying to enabling it will turn it off right away. E.g., code running on the platform setting SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_ENABLE_Msk; should change the clock source and enable the timer on real hardware, but resulted in an error in qemu. Signed-off-by: Richard Petri Reviewed-by: Peter Maydell Message-id: 20220201192650.289584-1-git@rpls.de Signed-off-by: Peter Maydell --- hw/timer/armv7m_systick.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/hw/timer/armv7m_systick.c b/hw/timer/armv7m_systick.c index 3bd951dd04..5dfe39afe3 100644 --- a/hw/timer/armv7m_systick.c +++ b/hw/timer/armv7m_systick.c @@ -149,6 +149,10 @@ static MemTxResult systick_write(void *opaque, hwaddr addr, s->control &= 0xfffffff8; s->control |= value & 7; + if ((oldval ^ value) & SYSTICK_CLKSOURCE) { + systick_set_period_from_clock(s); + } + if ((oldval ^ value) & SYSTICK_ENABLE) { if (value & SYSTICK_ENABLE) { ptimer_run(s->ptimer, 0); @@ -156,10 +160,6 @@ static MemTxResult systick_write(void *opaque, hwaddr addr, ptimer_stop(s->ptimer); } } - - if ((oldval ^ value) & SYSTICK_CLKSOURCE) { - systick_set_period_from_clock(s); - } ptimer_transaction_commit(s->ptimer); break; } From 43530095e18fd16dcd51a4b385ad2a22c36f5698 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Wed, 2 Feb 2022 12:16:02 +0100 Subject: [PATCH 280/460] hw/arm/smmuv3: Fix device reset We currently miss a bunch of register resets in the device reset function. This sometimes prevents the guest from rebooting after a system_reset (with virtio-blk-pci). For instance, we may get the following errors: invalid STE smmuv3-iommu-memory-region-0-0 translation failed for iova=0x13a9d2000(SMMU_EVT_C_BAD_STE) Invalid read at addr 0x13A9D2000, size 2, region '(null)', reason: rejected invalid STE smmuv3-iommu-memory-region-0-0 translation failed for iova=0x13a9d2000(SMMU_EVT_C_BAD_STE) Invalid write at addr 0x13A9D2000, size 2, region '(null)', reason: rejected invalid STE Signed-off-by: Eric Auger Message-id: 20220202111602.627429-1-eric.auger@redhat.com Fixes: 10a83cb988 ("hw/arm/smmuv3: Skeleton") Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/smmuv3.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c index 3b43368be0..674623aabe 100644 --- a/hw/arm/smmuv3.c +++ b/hw/arm/smmuv3.c @@ -278,6 +278,12 @@ static void smmuv3_init_regs(SMMUv3State *s) s->features = 0; s->sid_split = 0; s->aidr = 0x1; + s->cr[0] = 0; + s->cr0ack = 0; + s->irq_ctrl = 0; + s->gerror = 0; + s->gerrorn = 0; + s->statusr = 0; } static int smmu_get_ste(SMMUv3State *s, dma_addr_t addr, STE *buf, From b6f96009acc90a88db7f8913788f989b521eb938 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 1 Feb 2022 19:31:55 +0000 Subject: [PATCH 281/460] hw/intc/arm_gicv3_its: Use address_space_map() to access command queue packets Currently the ITS accesses each 8-byte doubleword in a 4-doubleword command packet with a separate address_space_ldq_le() call. This is awkward because the individual command processing functions have ended up with code to handle "load more doublewords out of the packet", which is both unwieldy and also a potential source of bugs because it's not obvious when looking at a line that pulls a field out of the 'value' variable which of the 4 doublewords that variable currently holds. Switch to using address_space_map() to map the whole command packet at once and fish the four doublewords out of it. Then each process_* function can start with a few lines of code that extract the fields it cares about. This requires us to split out the guts of process_its_cmd() into a new do_process_its_cmd(), because we were previously overloading the value and offset arguments as a backdoor way to directly pass the devid and eventid from a write to GITS_TRANSLATER. The new do_process_its_cmd() takes those arguments directly, and process_its_cmd() is just a wrapper that does the "read fields from command packet" part. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20220201193207.2771604-2-peter.maydell@linaro.org --- hw/intc/arm_gicv3_its.c | 208 +++++++++++---------------------------- hw/intc/gicv3_internal.h | 4 +- 2 files changed, 62 insertions(+), 150 deletions(-) diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c index 51d9be4ae6..b74753fb8f 100644 --- a/hw/intc/arm_gicv3_its.c +++ b/hw/intc/arm_gicv3_its.c @@ -224,11 +224,9 @@ static uint64_t get_dte(GICv3ITSState *s, uint32_t devid, MemTxResult *res) * 3. handling of ITS CLEAR command * 4. handling of ITS DISCARD command */ -static ItsCmdResult process_its_cmd(GICv3ITSState *s, uint64_t value, - uint32_t offset, ItsCmdType cmd) +static ItsCmdResult do_process_its_cmd(GICv3ITSState *s, uint32_t devid, + uint32_t eventid, ItsCmdType cmd) { - AddressSpace *as = &s->gicv3->dma_as; - uint32_t devid, eventid; MemTxResult res = MEMTX_OK; bool dte_valid; uint64_t dte = 0; @@ -240,22 +238,6 @@ static ItsCmdResult process_its_cmd(GICv3ITSState *s, uint64_t value, bool cte_valid = false; uint64_t rdbase; - if (cmd == NONE) { - devid = offset; - } else { - devid = ((value & DEVID_MASK) >> DEVID_SHIFT); - - offset += NUM_BYTES_IN_DW; - value = address_space_ldq_le(as, s->cq.base_addr + offset, - MEMTXATTRS_UNSPECIFIED, &res); - } - - if (res != MEMTX_OK) { - return CMD_STALL; - } - - eventid = (value & EVENTID_MASK); - if (devid >= s->dt.num_entries) { qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid command attributes: devid %d>=%d", @@ -342,11 +324,19 @@ static ItsCmdResult process_its_cmd(GICv3ITSState *s, uint64_t value, } return CMD_CONTINUE; } - -static ItsCmdResult process_mapti(GICv3ITSState *s, uint64_t value, - uint32_t offset, bool ignore_pInt) +static ItsCmdResult process_its_cmd(GICv3ITSState *s, const uint64_t *cmdpkt, + ItsCmdType cmd) +{ + uint32_t devid, eventid; + + devid = (cmdpkt[0] & DEVID_MASK) >> DEVID_SHIFT; + eventid = cmdpkt[1] & EVENTID_MASK; + return do_process_its_cmd(s, devid, eventid, cmd); +} + +static ItsCmdResult process_mapti(GICv3ITSState *s, const uint64_t *cmdpkt, + bool ignore_pInt) { - AddressSpace *as = &s->gicv3->dma_as; uint32_t devid, eventid; uint32_t pIntid = 0; uint64_t num_eventids; @@ -357,32 +347,16 @@ static ItsCmdResult process_mapti(GICv3ITSState *s, uint64_t value, uint64_t dte = 0; IteEntry ite = {}; - devid = ((value & DEVID_MASK) >> DEVID_SHIFT); - offset += NUM_BYTES_IN_DW; - value = address_space_ldq_le(as, s->cq.base_addr + offset, - MEMTXATTRS_UNSPECIFIED, &res); - - if (res != MEMTX_OK) { - return CMD_STALL; - } - - eventid = (value & EVENTID_MASK); + devid = (cmdpkt[0] & DEVID_MASK) >> DEVID_SHIFT; + eventid = cmdpkt[1] & EVENTID_MASK; if (ignore_pInt) { pIntid = eventid; } else { - pIntid = ((value & pINTID_MASK) >> pINTID_SHIFT); + pIntid = (cmdpkt[1] & pINTID_MASK) >> pINTID_SHIFT; } - offset += NUM_BYTES_IN_DW; - value = address_space_ldq_le(as, s->cq.base_addr + offset, - MEMTXATTRS_UNSPECIFIED, &res); - - if (res != MEMTX_OK) { - return CMD_STALL; - } - - icid = value & ICID_MASK; + icid = cmdpkt[2] & ICID_MASK; if (devid >= s->dt.num_entries) { qemu_log_mask(LOG_GUEST_ERROR, @@ -459,31 +433,18 @@ static bool update_cte(GICv3ITSState *s, uint16_t icid, bool valid, return res == MEMTX_OK; } -static ItsCmdResult process_mapc(GICv3ITSState *s, uint32_t offset) +static ItsCmdResult process_mapc(GICv3ITSState *s, const uint64_t *cmdpkt) { - AddressSpace *as = &s->gicv3->dma_as; uint16_t icid; uint64_t rdbase; bool valid; - MemTxResult res = MEMTX_OK; - uint64_t value; - offset += NUM_BYTES_IN_DW; - offset += NUM_BYTES_IN_DW; + icid = cmdpkt[2] & ICID_MASK; - value = address_space_ldq_le(as, s->cq.base_addr + offset, - MEMTXATTRS_UNSPECIFIED, &res); - - if (res != MEMTX_OK) { - return CMD_STALL; - } - - icid = value & ICID_MASK; - - rdbase = (value & R_MAPC_RDBASE_MASK) >> R_MAPC_RDBASE_SHIFT; + rdbase = (cmdpkt[2] & R_MAPC_RDBASE_MASK) >> R_MAPC_RDBASE_SHIFT; rdbase &= RDBASE_PROCNUM_MASK; - valid = (value & CMD_FIELD_VALID_MASK); + valid = cmdpkt[2] & CMD_FIELD_VALID_MASK; if ((icid >= s->ct.num_entries) || (rdbase >= s->gicv3->num_cpu)) { qemu_log_mask(LOG_GUEST_ERROR, @@ -532,39 +493,17 @@ static bool update_dte(GICv3ITSState *s, uint32_t devid, bool valid, return res == MEMTX_OK; } -static ItsCmdResult process_mapd(GICv3ITSState *s, uint64_t value, - uint32_t offset) +static ItsCmdResult process_mapd(GICv3ITSState *s, const uint64_t *cmdpkt) { - AddressSpace *as = &s->gicv3->dma_as; uint32_t devid; uint8_t size; uint64_t itt_addr; bool valid; - MemTxResult res = MEMTX_OK; - devid = ((value & DEVID_MASK) >> DEVID_SHIFT); - - offset += NUM_BYTES_IN_DW; - value = address_space_ldq_le(as, s->cq.base_addr + offset, - MEMTXATTRS_UNSPECIFIED, &res); - - if (res != MEMTX_OK) { - return CMD_STALL; - } - - size = (value & SIZE_MASK); - - offset += NUM_BYTES_IN_DW; - value = address_space_ldq_le(as, s->cq.base_addr + offset, - MEMTXATTRS_UNSPECIFIED, &res); - - if (res != MEMTX_OK) { - return CMD_STALL; - } - - itt_addr = (value & ITTADDR_MASK) >> ITTADDR_SHIFT; - - valid = (value & CMD_FIELD_VALID_MASK); + devid = (cmdpkt[0] & DEVID_MASK) >> DEVID_SHIFT; + size = cmdpkt[1] & SIZE_MASK; + itt_addr = (cmdpkt[2] & ITTADDR_MASK) >> ITTADDR_SHIFT; + valid = cmdpkt[2] & CMD_FIELD_VALID_MASK; if ((devid >= s->dt.num_entries) || (size > FIELD_EX64(s->typer, GITS_TYPER, IDBITS))) { @@ -582,23 +521,13 @@ static ItsCmdResult process_mapd(GICv3ITSState *s, uint64_t value, return update_dte(s, devid, valid, size, itt_addr) ? CMD_CONTINUE : CMD_STALL; } -static ItsCmdResult process_movall(GICv3ITSState *s, uint64_t value, - uint32_t offset) +static ItsCmdResult process_movall(GICv3ITSState *s, const uint64_t *cmdpkt) { - AddressSpace *as = &s->gicv3->dma_as; - MemTxResult res = MEMTX_OK; uint64_t rd1, rd2; - /* No fields in dwords 0 or 1 */ - offset += NUM_BYTES_IN_DW; - offset += NUM_BYTES_IN_DW; - value = address_space_ldq_le(as, s->cq.base_addr + offset, - MEMTXATTRS_UNSPECIFIED, &res); - if (res != MEMTX_OK) { - return CMD_STALL; - } + rd1 = FIELD_EX64(cmdpkt[2], MOVALL_2, RDBASE1); + rd2 = FIELD_EX64(cmdpkt[3], MOVALL_3, RDBASE2); - rd1 = FIELD_EX64(value, MOVALL_2, RDBASE1); if (rd1 >= s->gicv3->num_cpu) { qemu_log_mask(LOG_GUEST_ERROR, "%s: RDBASE1 %" PRId64 @@ -606,15 +535,6 @@ static ItsCmdResult process_movall(GICv3ITSState *s, uint64_t value, __func__, rd1, s->gicv3->num_cpu); return CMD_CONTINUE; } - - offset += NUM_BYTES_IN_DW; - value = address_space_ldq_le(as, s->cq.base_addr + offset, - MEMTXATTRS_UNSPECIFIED, &res); - if (res != MEMTX_OK) { - return CMD_STALL; - } - - rd2 = FIELD_EX64(value, MOVALL_3, RDBASE2); if (rd2 >= s->gicv3->num_cpu) { qemu_log_mask(LOG_GUEST_ERROR, "%s: RDBASE2 %" PRId64 @@ -634,10 +554,8 @@ static ItsCmdResult process_movall(GICv3ITSState *s, uint64_t value, return CMD_CONTINUE; } -static ItsCmdResult process_movi(GICv3ITSState *s, uint64_t value, - uint32_t offset) +static ItsCmdResult process_movi(GICv3ITSState *s, const uint64_t *cmdpkt) { - AddressSpace *as = &s->gicv3->dma_as; MemTxResult res = MEMTX_OK; uint32_t devid, eventid, intid; uint16_t old_icid, new_icid; @@ -648,23 +566,9 @@ static ItsCmdResult process_movi(GICv3ITSState *s, uint64_t value, uint64_t num_eventids; IteEntry ite = {}; - devid = FIELD_EX64(value, MOVI_0, DEVICEID); - - offset += NUM_BYTES_IN_DW; - value = address_space_ldq_le(as, s->cq.base_addr + offset, - MEMTXATTRS_UNSPECIFIED, &res); - if (res != MEMTX_OK) { - return CMD_STALL; - } - eventid = FIELD_EX64(value, MOVI_1, EVENTID); - - offset += NUM_BYTES_IN_DW; - value = address_space_ldq_le(as, s->cq.base_addr + offset, - MEMTXATTRS_UNSPECIFIED, &res); - if (res != MEMTX_OK) { - return CMD_STALL; - } - new_icid = FIELD_EX64(value, MOVI_2, ICID); + devid = FIELD_EX64(cmdpkt[0], MOVI_0, DEVICEID); + eventid = FIELD_EX64(cmdpkt[1], MOVI_1, EVENTID); + new_icid = FIELD_EX64(cmdpkt[2], MOVI_2, ICID); if (devid >= s->dt.num_entries) { qemu_log_mask(LOG_GUEST_ERROR, @@ -786,9 +690,7 @@ static void process_cmdq(GICv3ITSState *s) uint32_t wr_offset = 0; uint32_t rd_offset = 0; uint32_t cq_offset = 0; - uint64_t data; AddressSpace *as = &s->gicv3->dma_as; - MemTxResult res = MEMTX_OK; uint8_t cmd; int i; @@ -816,28 +718,40 @@ static void process_cmdq(GICv3ITSState *s) while (wr_offset != rd_offset) { ItsCmdResult result = CMD_CONTINUE; + void *hostmem; + hwaddr buflen; + uint64_t cmdpkt[GITS_CMDQ_ENTRY_WORDS]; cq_offset = (rd_offset * GITS_CMDQ_ENTRY_SIZE); - data = address_space_ldq_le(as, s->cq.base_addr + cq_offset, - MEMTXATTRS_UNSPECIFIED, &res); - if (res != MEMTX_OK) { + + buflen = GITS_CMDQ_ENTRY_SIZE; + hostmem = address_space_map(as, s->cq.base_addr + cq_offset, + &buflen, false, MEMTXATTRS_UNSPECIFIED); + if (!hostmem || buflen != GITS_CMDQ_ENTRY_SIZE) { + if (hostmem) { + address_space_unmap(as, hostmem, buflen, false, 0); + } s->creadr = FIELD_DP64(s->creadr, GITS_CREADR, STALLED, 1); qemu_log_mask(LOG_GUEST_ERROR, "%s: could not read command at 0x%" PRIx64 "\n", __func__, s->cq.base_addr + cq_offset); break; } + for (i = 0; i < ARRAY_SIZE(cmdpkt); i++) { + cmdpkt[i] = ldq_le_p(hostmem + i * sizeof(uint64_t)); + } + address_space_unmap(as, hostmem, buflen, false, 0); - cmd = (data & CMD_MASK); + cmd = cmdpkt[0] & CMD_MASK; trace_gicv3_its_process_command(rd_offset, cmd); switch (cmd) { case GITS_CMD_INT: - result = process_its_cmd(s, data, cq_offset, INTERRUPT); + result = process_its_cmd(s, cmdpkt, INTERRUPT); break; case GITS_CMD_CLEAR: - result = process_its_cmd(s, data, cq_offset, CLEAR); + result = process_its_cmd(s, cmdpkt, CLEAR); break; case GITS_CMD_SYNC: /* @@ -848,19 +762,19 @@ static void process_cmdq(GICv3ITSState *s) */ break; case GITS_CMD_MAPD: - result = process_mapd(s, data, cq_offset); + result = process_mapd(s, cmdpkt); break; case GITS_CMD_MAPC: - result = process_mapc(s, cq_offset); + result = process_mapc(s, cmdpkt); break; case GITS_CMD_MAPTI: - result = process_mapti(s, data, cq_offset, false); + result = process_mapti(s, cmdpkt, false); break; case GITS_CMD_MAPI: - result = process_mapti(s, data, cq_offset, true); + result = process_mapti(s, cmdpkt, true); break; case GITS_CMD_DISCARD: - result = process_its_cmd(s, data, cq_offset, DISCARD); + result = process_its_cmd(s, cmdpkt, DISCARD); break; case GITS_CMD_INV: case GITS_CMD_INVALL: @@ -875,10 +789,10 @@ static void process_cmdq(GICv3ITSState *s) } break; case GITS_CMD_MOVI: - result = process_movi(s, data, cq_offset); + result = process_movi(s, cmdpkt); break; case GITS_CMD_MOVALL: - result = process_movall(s, data, cq_offset); + result = process_movall(s, cmdpkt); break; default: break; @@ -1032,15 +946,13 @@ static MemTxResult gicv3_its_translation_write(void *opaque, hwaddr offset, { GICv3ITSState *s = (GICv3ITSState *)opaque; bool result = true; - uint32_t devid = 0; trace_gicv3_its_translation_write(offset, data, size, attrs.requester_id); switch (offset) { case GITS_TRANSLATER: if (s->ctlr & R_GITS_CTLR_ENABLED_MASK) { - devid = attrs.requester_id; - result = process_its_cmd(s, data, devid, NONE); + result = do_process_its_cmd(s, attrs.requester_id, data, NONE); } break; default: diff --git a/hw/intc/gicv3_internal.h b/hw/intc/gicv3_internal.h index b1af26df9f..60c8617e4e 100644 --- a/hw/intc/gicv3_internal.h +++ b/hw/intc/gicv3_internal.h @@ -309,8 +309,8 @@ FIELD(GITS_TYPER, CIL, 36, 1) #define LPI_CTE_ENABLED TABLE_ENTRY_VALID_MASK #define LPI_PRIORITY_MASK 0xfc -#define GITS_CMDQ_ENTRY_SIZE 32 -#define NUM_BYTES_IN_DW 8 +#define GITS_CMDQ_ENTRY_WORDS 4 +#define GITS_CMDQ_ENTRY_SIZE (GITS_CMDQ_ENTRY_WORDS * sizeof(uint64_t)) #define CMD_MASK 0xff From 4acf93e193af22556c21ec2d94247dc09f28d75a Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 1 Feb 2022 19:31:56 +0000 Subject: [PATCH 282/460] hw/intc/arm_gicv3_its: Keep DTEs as a struct, not a raw uint64_t In the ITS, a DTE is an entry in the device table, which contains multiple fields. Currently the function get_dte() which reads one entry from the device table returns it as a raw 64-bit integer, which we then pass around in that form, only extracting fields from it as we need them. Create a real C struct with the same fields as the DTE, and populate it in get_dte(), so that that function and update_dte() are the only ones that need to care about the in-guest-memory format of the DTE. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20220201193207.2771604-3-peter.maydell@linaro.org --- hw/intc/arm_gicv3_its.c | 111 ++++++++++++++++++++-------------------- 1 file changed, 56 insertions(+), 55 deletions(-) diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c index b74753fb8f..6d70d7d59e 100644 --- a/hw/intc/arm_gicv3_its.c +++ b/hw/intc/arm_gicv3_its.c @@ -46,6 +46,12 @@ typedef struct { uint64_t itel; } IteEntry; +typedef struct DTEntry { + bool valid; + unsigned size; + uint64_t ittaddr; +} DTEntry; + /* * The ITS spec permits a range of CONSTRAINED UNPREDICTABLE options * if a command parameter is not correct. These include both "stall @@ -143,22 +149,18 @@ static bool get_cte(GICv3ITSState *s, uint16_t icid, uint64_t *cte, return FIELD_EX64(*cte, CTE, VALID); } -static bool update_ite(GICv3ITSState *s, uint32_t eventid, uint64_t dte, +static bool update_ite(GICv3ITSState *s, uint32_t eventid, const DTEntry *dte, IteEntry ite) { AddressSpace *as = &s->gicv3->dma_as; - uint64_t itt_addr; MemTxResult res = MEMTX_OK; - itt_addr = FIELD_EX64(dte, DTE, ITTADDR); - itt_addr <<= ITTADDR_SHIFT; /* 256 byte aligned */ - - address_space_stq_le(as, itt_addr + (eventid * (sizeof(uint64_t) + + address_space_stq_le(as, dte->ittaddr + (eventid * (sizeof(uint64_t) + sizeof(uint32_t))), ite.itel, MEMTXATTRS_UNSPECIFIED, &res); if (res == MEMTX_OK) { - address_space_stl_le(as, itt_addr + (eventid * (sizeof(uint64_t) + + address_space_stl_le(as, dte->ittaddr + (eventid * (sizeof(uint64_t) + sizeof(uint32_t))) + sizeof(uint32_t), ite.iteh, MEMTXATTRS_UNSPECIFIED, &res); } @@ -169,24 +171,20 @@ static bool update_ite(GICv3ITSState *s, uint32_t eventid, uint64_t dte, } } -static bool get_ite(GICv3ITSState *s, uint32_t eventid, uint64_t dte, +static bool get_ite(GICv3ITSState *s, uint32_t eventid, const DTEntry *dte, uint16_t *icid, uint32_t *pIntid, MemTxResult *res) { AddressSpace *as = &s->gicv3->dma_as; - uint64_t itt_addr; bool status = false; IteEntry ite = {}; - itt_addr = FIELD_EX64(dte, DTE, ITTADDR); - itt_addr <<= ITTADDR_SHIFT; /* 256 byte aligned */ - - ite.itel = address_space_ldq_le(as, itt_addr + + ite.itel = address_space_ldq_le(as, dte->ittaddr + (eventid * (sizeof(uint64_t) + sizeof(uint32_t))), MEMTXATTRS_UNSPECIFIED, res); if (*res == MEMTX_OK) { - ite.iteh = address_space_ldl_le(as, itt_addr + + ite.iteh = address_space_ldl_le(as, dte->ittaddr + (eventid * (sizeof(uint64_t) + sizeof(uint32_t))) + sizeof(uint32_t), MEMTXATTRS_UNSPECIFIED, res); @@ -205,15 +203,33 @@ static bool get_ite(GICv3ITSState *s, uint32_t eventid, uint64_t dte, return status; } -static uint64_t get_dte(GICv3ITSState *s, uint32_t devid, MemTxResult *res) +/* + * Read the Device Table entry at index @devid. On success (including + * successfully determining that there is no valid DTE for this index), + * we return MEMTX_OK and populate the DTEntry struct accordingly. + * If there is an error reading memory then we return the error code. + */ +static MemTxResult get_dte(GICv3ITSState *s, uint32_t devid, DTEntry *dte) { + MemTxResult res = MEMTX_OK; AddressSpace *as = &s->gicv3->dma_as; - uint64_t entry_addr = table_entry_addr(s, &s->dt, devid, res); + uint64_t entry_addr = table_entry_addr(s, &s->dt, devid, &res); + uint64_t dteval; if (entry_addr == -1) { - return 0; /* a DTE entry with the Valid bit clear */ + /* No L2 table entry, i.e. no valid DTE, or a memory error */ + dte->valid = false; + return res; } - return address_space_ldq_le(as, entry_addr, MEMTXATTRS_UNSPECIFIED, res); + dteval = address_space_ldq_le(as, entry_addr, MEMTXATTRS_UNSPECIFIED, &res); + if (res != MEMTX_OK) { + return res; + } + dte->valid = FIELD_EX64(dteval, DTE, VALID); + dte->size = FIELD_EX64(dteval, DTE, SIZE); + /* DTE word field stores bits [51:8] of the ITT address */ + dte->ittaddr = FIELD_EX64(dteval, DTE, ITTADDR) << ITTADDR_SHIFT; + return MEMTX_OK; } /* @@ -228,8 +244,6 @@ static ItsCmdResult do_process_its_cmd(GICv3ITSState *s, uint32_t devid, uint32_t eventid, ItsCmdType cmd) { MemTxResult res = MEMTX_OK; - bool dte_valid; - uint64_t dte = 0; uint64_t num_eventids; uint16_t icid = 0; uint32_t pIntid = 0; @@ -237,6 +251,7 @@ static ItsCmdResult do_process_its_cmd(GICv3ITSState *s, uint32_t devid, uint64_t cte = 0; bool cte_valid = false; uint64_t rdbase; + DTEntry dte; if (devid >= s->dt.num_entries) { qemu_log_mask(LOG_GUEST_ERROR, @@ -245,23 +260,17 @@ static ItsCmdResult do_process_its_cmd(GICv3ITSState *s, uint32_t devid, return CMD_CONTINUE; } - dte = get_dte(s, devid, &res); - - if (res != MEMTX_OK) { + if (get_dte(s, devid, &dte) != MEMTX_OK) { return CMD_STALL; } - dte_valid = FIELD_EX64(dte, DTE, VALID); - - if (!dte_valid) { + if (!dte.valid) { qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid command attributes: " - "invalid dte: %"PRIx64" for %d\n", - __func__, dte, devid); + "invalid dte for %d\n", __func__, devid); return CMD_CONTINUE; } - num_eventids = 1ULL << (FIELD_EX64(dte, DTE, SIZE) + 1); - + num_eventids = 1ULL << (dte.size + 1); if (eventid >= num_eventids) { qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid command attributes: eventid %d >= %" @@ -270,7 +279,7 @@ static ItsCmdResult do_process_its_cmd(GICv3ITSState *s, uint32_t devid, return CMD_CONTINUE; } - ite_valid = get_ite(s, eventid, dte, &icid, &pIntid, &res); + ite_valid = get_ite(s, eventid, &dte, &icid, &pIntid, &res); if (res != MEMTX_OK) { return CMD_STALL; } @@ -320,7 +329,7 @@ static ItsCmdResult do_process_its_cmd(GICv3ITSState *s, uint32_t devid, if (cmd == DISCARD) { IteEntry ite = {}; /* remove mapping from interrupt translation table */ - return update_ite(s, eventid, dte, ite) ? CMD_CONTINUE : CMD_STALL; + return update_ite(s, eventid, &dte, ite) ? CMD_CONTINUE : CMD_STALL; } return CMD_CONTINUE; } @@ -341,11 +350,9 @@ static ItsCmdResult process_mapti(GICv3ITSState *s, const uint64_t *cmdpkt, uint32_t pIntid = 0; uint64_t num_eventids; uint32_t num_intids; - bool dte_valid; - MemTxResult res = MEMTX_OK; uint16_t icid = 0; - uint64_t dte = 0; IteEntry ite = {}; + DTEntry dte; devid = (cmdpkt[0] & DEVID_MASK) >> DEVID_SHIFT; eventid = cmdpkt[1] & EVENTID_MASK; @@ -365,24 +372,21 @@ static ItsCmdResult process_mapti(GICv3ITSState *s, const uint64_t *cmdpkt, return CMD_CONTINUE; } - dte = get_dte(s, devid, &res); - - if (res != MEMTX_OK) { + if (get_dte(s, devid, &dte) != MEMTX_OK) { return CMD_STALL; } - dte_valid = FIELD_EX64(dte, DTE, VALID); - num_eventids = 1ULL << (FIELD_EX64(dte, DTE, SIZE) + 1); + num_eventids = 1ULL << (dte.size + 1); num_intids = 1ULL << (GICD_TYPER_IDBITS + 1); if ((icid >= s->ct.num_entries) - || !dte_valid || (eventid >= num_eventids) || + || !dte.valid || (eventid >= num_eventids) || (((pIntid < GICV3_LPI_INTID_START) || (pIntid >= num_intids)) && (pIntid != INTID_SPURIOUS))) { qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid command attributes " "icid %d or eventid %d or pIntid %d or" "unmapped dte %d\n", __func__, icid, eventid, - pIntid, dte_valid); + pIntid, dte.valid); /* * in this implementation, in case of error * we ignore this command and move onto the next @@ -392,13 +396,13 @@ static ItsCmdResult process_mapti(GICv3ITSState *s, const uint64_t *cmdpkt, } /* add ite entry to interrupt translation table */ - ite.itel = FIELD_DP64(ite.itel, ITE_L, VALID, dte_valid); + ite.itel = FIELD_DP64(ite.itel, ITE_L, VALID, true); ite.itel = FIELD_DP64(ite.itel, ITE_L, INTTYPE, ITE_INTTYPE_PHYSICAL); ite.itel = FIELD_DP64(ite.itel, ITE_L, INTID, pIntid); ite.itel = FIELD_DP64(ite.itel, ITE_L, DOORBELL, INTID_SPURIOUS); ite.iteh = FIELD_DP32(ite.iteh, ITE_H, ICID, icid); - return update_ite(s, eventid, dte, ite) ? CMD_CONTINUE : CMD_STALL; + return update_ite(s, eventid, &dte, ite) ? CMD_CONTINUE : CMD_STALL; } static bool update_cte(GICv3ITSState *s, uint16_t icid, bool valid, @@ -561,10 +565,10 @@ static ItsCmdResult process_movi(GICv3ITSState *s, const uint64_t *cmdpkt) uint16_t old_icid, new_icid; uint64_t old_cte, new_cte; uint64_t old_rdbase, new_rdbase; - uint64_t dte; - bool dte_valid, ite_valid, cte_valid; + bool ite_valid, cte_valid; uint64_t num_eventids; IteEntry ite = {}; + DTEntry dte; devid = FIELD_EX64(cmdpkt[0], MOVI_0, DEVICEID); eventid = FIELD_EX64(cmdpkt[1], MOVI_1, EVENTID); @@ -576,21 +580,18 @@ static ItsCmdResult process_movi(GICv3ITSState *s, const uint64_t *cmdpkt) __func__, devid, s->dt.num_entries); return CMD_CONTINUE; } - dte = get_dte(s, devid, &res); - if (res != MEMTX_OK) { + if (get_dte(s, devid, &dte) != MEMTX_OK) { return CMD_STALL; } - dte_valid = FIELD_EX64(dte, DTE, VALID); - if (!dte_valid) { + if (!dte.valid) { qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid command attributes: " - "invalid dte: %"PRIx64" for %d\n", - __func__, dte, devid); + "invalid dte for %d\n", __func__, devid); return CMD_CONTINUE; } - num_eventids = 1ULL << (FIELD_EX64(dte, DTE, SIZE) + 1); + num_eventids = 1ULL << (dte.size + 1); if (eventid >= num_eventids) { qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid command attributes: eventid %d >= %" @@ -599,7 +600,7 @@ static ItsCmdResult process_movi(GICv3ITSState *s, const uint64_t *cmdpkt) return CMD_CONTINUE; } - ite_valid = get_ite(s, eventid, dte, &old_icid, &intid, &res); + ite_valid = get_ite(s, eventid, &dte, &old_icid, &intid, &res); if (res != MEMTX_OK) { return CMD_STALL; } @@ -678,7 +679,7 @@ static ItsCmdResult process_movi(GICv3ITSState *s, const uint64_t *cmdpkt) ite.itel = FIELD_DP64(ite.itel, ITE_L, INTID, intid); ite.itel = FIELD_DP64(ite.itel, ITE_L, DOORBELL, INTID_SPURIOUS); ite.iteh = FIELD_DP32(ite.iteh, ITE_H, ICID, new_icid); - return update_ite(s, eventid, dte, ite) ? CMD_CONTINUE : CMD_STALL; + return update_ite(s, eventid, &dte, ite) ? CMD_CONTINUE : CMD_STALL; } /* From 22d62b08ba7a3909d42f4fdf097eb2ae8c9c00e3 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 1 Feb 2022 19:31:57 +0000 Subject: [PATCH 283/460] hw/intc/arm_gicv3_its: Pass DTEntry to update_dte() Make update_dte() take a DTEntry struct rather than all the fields of the new DTE as separate arguments. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20220201193207.2771604-4-peter.maydell@linaro.org --- hw/intc/arm_gicv3_its.c | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c index 6d70d7d59e..1856210e79 100644 --- a/hw/intc/arm_gicv3_its.c +++ b/hw/intc/arm_gicv3_its.c @@ -465,20 +465,23 @@ static ItsCmdResult process_mapc(GICv3ITSState *s, const uint64_t *cmdpkt) return update_cte(s, icid, valid, rdbase) ? CMD_CONTINUE : CMD_STALL; } -static bool update_dte(GICv3ITSState *s, uint32_t devid, bool valid, - uint8_t size, uint64_t itt_addr) +/* + * Update the Device Table entry for @devid to @dte. Returns true + * on success, false if there was a memory access error. + */ +static bool update_dte(GICv3ITSState *s, uint32_t devid, const DTEntry *dte) { AddressSpace *as = &s->gicv3->dma_as; uint64_t entry_addr; - uint64_t dte = 0; + uint64_t dteval = 0; MemTxResult res = MEMTX_OK; if (s->dt.valid) { - if (valid) { + if (dte->valid) { /* add mapping entry to device table */ - dte = FIELD_DP64(dte, DTE, VALID, 1); - dte = FIELD_DP64(dte, DTE, SIZE, size); - dte = FIELD_DP64(dte, DTE, ITTADDR, itt_addr); + dteval = FIELD_DP64(dteval, DTE, VALID, 1); + dteval = FIELD_DP64(dteval, DTE, SIZE, dte->size); + dteval = FIELD_DP64(dteval, DTE, ITTADDR, dte->ittaddr); } } else { return true; @@ -493,27 +496,25 @@ static bool update_dte(GICv3ITSState *s, uint32_t devid, bool valid, /* No L2 table for this index: discard write and continue */ return true; } - address_space_stq_le(as, entry_addr, dte, MEMTXATTRS_UNSPECIFIED, &res); + address_space_stq_le(as, entry_addr, dteval, MEMTXATTRS_UNSPECIFIED, &res); return res == MEMTX_OK; } static ItsCmdResult process_mapd(GICv3ITSState *s, const uint64_t *cmdpkt) { uint32_t devid; - uint8_t size; - uint64_t itt_addr; - bool valid; + DTEntry dte; devid = (cmdpkt[0] & DEVID_MASK) >> DEVID_SHIFT; - size = cmdpkt[1] & SIZE_MASK; - itt_addr = (cmdpkt[2] & ITTADDR_MASK) >> ITTADDR_SHIFT; - valid = cmdpkt[2] & CMD_FIELD_VALID_MASK; + dte.size = cmdpkt[1] & SIZE_MASK; + dte.ittaddr = (cmdpkt[2] & ITTADDR_MASK) >> ITTADDR_SHIFT; + dte.valid = cmdpkt[2] & CMD_FIELD_VALID_MASK; if ((devid >= s->dt.num_entries) || - (size > FIELD_EX64(s->typer, GITS_TYPER, IDBITS))) { + (dte.size > FIELD_EX64(s->typer, GITS_TYPER, IDBITS))) { qemu_log_mask(LOG_GUEST_ERROR, "ITS MAPD: invalid device table attributes " - "devid %d or size %d\n", devid, size); + "devid %d or size %d\n", devid, dte.size); /* * in this implementation, in case of error * we ignore this command and move onto the next @@ -522,7 +523,7 @@ static ItsCmdResult process_mapd(GICv3ITSState *s, const uint64_t *cmdpkt) return CMD_CONTINUE; } - return update_dte(s, devid, valid, size, itt_addr) ? CMD_CONTINUE : CMD_STALL; + return update_dte(s, devid, &dte) ? CMD_CONTINUE : CMD_STALL; } static ItsCmdResult process_movall(GICv3ITSState *s, const uint64_t *cmdpkt) From d37cf49b11f3ef78c3265a941faf320ee4f96bdf Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 1 Feb 2022 19:31:58 +0000 Subject: [PATCH 284/460] hw/intc/arm_gicv3_its: Keep CTEs as a struct, not a raw uint64_t In the ITS, a CTE is an entry in the collection table, which contains multiple fields. Currently the function get_cte() which reads one entry from the device table returns a success/failure boolean and passes back the raw 64-bit integer CTE value via a pointer argument. We then extract fields from the CTE as we need them. Create a real C struct with the same fields as the CTE, and populate it in get_cte(), so that that function and update_cte() are the only ones which need to care about the in-guest-memory format of the CTE. This brings get_cte()'s API into line with get_dte(). Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20220201193207.2771604-5-peter.maydell@linaro.org --- hw/intc/arm_gicv3_its.c | 96 ++++++++++++++++++++++------------------- 1 file changed, 52 insertions(+), 44 deletions(-) diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c index 1856210e79..482a71ba73 100644 --- a/hw/intc/arm_gicv3_its.c +++ b/hw/intc/arm_gicv3_its.c @@ -52,6 +52,11 @@ typedef struct DTEntry { uint64_t ittaddr; } DTEntry; +typedef struct CTEntry { + bool valid; + uint32_t rdbase; +} CTEntry; + /* * The ITS spec permits a range of CONSTRAINED UNPREDICTABLE options * if a command parameter is not correct. These include both "stall @@ -135,18 +140,32 @@ static uint64_t table_entry_addr(GICv3ITSState *s, TableDesc *td, return (l2 & ((1ULL << 51) - 1)) + (idx % num_l2_entries) * td->entry_sz; } -static bool get_cte(GICv3ITSState *s, uint16_t icid, uint64_t *cte, - MemTxResult *res) +/* + * Read the Collection Table entry at index @icid. On success (including + * successfully determining that there is no valid CTE for this index), + * we return MEMTX_OK and populate the CTEntry struct @cte accordingly. + * If there is an error reading memory then we return the error code. + */ +static MemTxResult get_cte(GICv3ITSState *s, uint16_t icid, CTEntry *cte) { AddressSpace *as = &s->gicv3->dma_as; - uint64_t entry_addr = table_entry_addr(s, &s->ct, icid, res); + MemTxResult res = MEMTX_OK; + uint64_t entry_addr = table_entry_addr(s, &s->ct, icid, &res); + uint64_t cteval; if (entry_addr == -1) { - return false; /* not valid */ + /* No L2 table entry, i.e. no valid CTE, or a memory error */ + cte->valid = false; + return res; } - *cte = address_space_ldq_le(as, entry_addr, MEMTXATTRS_UNSPECIFIED, res); - return FIELD_EX64(*cte, CTE, VALID); + cteval = address_space_ldq_le(as, entry_addr, MEMTXATTRS_UNSPECIFIED, &res); + if (res != MEMTX_OK) { + return res; + } + cte->valid = FIELD_EX64(cteval, CTE, VALID); + cte->rdbase = FIELD_EX64(cteval, CTE, RDBASE); + return MEMTX_OK; } static bool update_ite(GICv3ITSState *s, uint32_t eventid, const DTEntry *dte, @@ -248,10 +267,8 @@ static ItsCmdResult do_process_its_cmd(GICv3ITSState *s, uint32_t devid, uint16_t icid = 0; uint32_t pIntid = 0; bool ite_valid = false; - uint64_t cte = 0; - bool cte_valid = false; - uint64_t rdbase; DTEntry dte; + CTEntry cte; if (devid >= s->dt.num_entries) { qemu_log_mask(LOG_GUEST_ERROR, @@ -298,15 +315,13 @@ static ItsCmdResult do_process_its_cmd(GICv3ITSState *s, uint32_t devid, return CMD_CONTINUE; } - cte_valid = get_cte(s, icid, &cte, &res); - if (res != MEMTX_OK) { + if (get_cte(s, icid, &cte) != MEMTX_OK) { return CMD_STALL; } - if (!cte_valid) { + if (!cte.valid) { qemu_log_mask(LOG_GUEST_ERROR, - "%s: invalid command attributes: " - "invalid cte: %"PRIx64"\n", - __func__, cte); + "%s: invalid command attributes: invalid CTE\n", + __func__); return CMD_CONTINUE; } @@ -314,16 +329,14 @@ static ItsCmdResult do_process_its_cmd(GICv3ITSState *s, uint32_t devid, * Current implementation only supports rdbase == procnum * Hence rdbase physical address is ignored */ - rdbase = FIELD_EX64(cte, CTE, RDBASE); - - if (rdbase >= s->gicv3->num_cpu) { + if (cte.rdbase >= s->gicv3->num_cpu) { return CMD_CONTINUE; } if ((cmd == CLEAR) || (cmd == DISCARD)) { - gicv3_redist_process_lpi(&s->gicv3->cpu[rdbase], pIntid, 0); + gicv3_redist_process_lpi(&s->gicv3->cpu[cte.rdbase], pIntid, 0); } else { - gicv3_redist_process_lpi(&s->gicv3->cpu[rdbase], pIntid, 1); + gicv3_redist_process_lpi(&s->gicv3->cpu[cte.rdbase], pIntid, 1); } if (cmd == DISCARD) { @@ -564,12 +577,11 @@ static ItsCmdResult process_movi(GICv3ITSState *s, const uint64_t *cmdpkt) MemTxResult res = MEMTX_OK; uint32_t devid, eventid, intid; uint16_t old_icid, new_icid; - uint64_t old_cte, new_cte; - uint64_t old_rdbase, new_rdbase; - bool ite_valid, cte_valid; + bool ite_valid; uint64_t num_eventids; IteEntry ite = {}; DTEntry dte; + CTEntry old_cte, new_cte; devid = FIELD_EX64(cmdpkt[0], MOVI_0, DEVICEID); eventid = FIELD_EX64(cmdpkt[1], MOVI_1, EVENTID); @@ -627,50 +639,46 @@ static ItsCmdResult process_movi(GICv3ITSState *s, const uint64_t *cmdpkt) return CMD_CONTINUE; } - cte_valid = get_cte(s, old_icid, &old_cte, &res); - if (res != MEMTX_OK) { + if (get_cte(s, old_icid, &old_cte) != MEMTX_OK) { return CMD_STALL; } - if (!cte_valid) { + if (!old_cte.valid) { qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid command attributes: " - "invalid cte: %"PRIx64"\n", - __func__, old_cte); + "invalid CTE for old ICID 0x%x\n", + __func__, old_icid); return CMD_CONTINUE; } - cte_valid = get_cte(s, new_icid, &new_cte, &res); - if (res != MEMTX_OK) { + if (get_cte(s, new_icid, &new_cte) != MEMTX_OK) { return CMD_STALL; } - if (!cte_valid) { + if (!new_cte.valid) { qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid command attributes: " - "invalid cte: %"PRIx64"\n", - __func__, new_cte); + "invalid CTE for new ICID 0x%x\n", + __func__, new_icid); return CMD_CONTINUE; } - old_rdbase = FIELD_EX64(old_cte, CTE, RDBASE); - if (old_rdbase >= s->gicv3->num_cpu) { + if (old_cte.rdbase >= s->gicv3->num_cpu) { qemu_log_mask(LOG_GUEST_ERROR, - "%s: CTE has invalid rdbase 0x%"PRIx64"\n", - __func__, old_rdbase); + "%s: CTE has invalid rdbase 0x%x\n", + __func__, old_cte.rdbase); return CMD_CONTINUE; } - new_rdbase = FIELD_EX64(new_cte, CTE, RDBASE); - if (new_rdbase >= s->gicv3->num_cpu) { + if (new_cte.rdbase >= s->gicv3->num_cpu) { qemu_log_mask(LOG_GUEST_ERROR, - "%s: CTE has invalid rdbase 0x%"PRIx64"\n", - __func__, new_rdbase); + "%s: CTE has invalid rdbase 0x%x\n", + __func__, new_cte.rdbase); return CMD_CONTINUE; } - if (old_rdbase != new_rdbase) { + if (old_cte.rdbase != new_cte.rdbase) { /* Move the LPI from the old redistributor to the new one */ - gicv3_redist_mov_lpi(&s->gicv3->cpu[old_rdbase], - &s->gicv3->cpu[new_rdbase], + gicv3_redist_mov_lpi(&s->gicv3->cpu[old_cte.rdbase], + &s->gicv3->cpu[new_cte.rdbase], intid); } From 06985cc3fe5a5a7577ddd43406758aa79099b4f7 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 1 Feb 2022 19:31:59 +0000 Subject: [PATCH 285/460] hw/intc/arm_gicv3_its: Pass CTEntry to update_cte() Make update_cte() take a CTEntry struct rather than all the fields of the new CTE as separate arguments. This brings it into line with the update_dte() API. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20220201193207.2771604-6-peter.maydell@linaro.org --- hw/intc/arm_gicv3_its.c | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c index 482a71ba73..b94775fd37 100644 --- a/hw/intc/arm_gicv3_its.c +++ b/hw/intc/arm_gicv3_its.c @@ -418,22 +418,25 @@ static ItsCmdResult process_mapti(GICv3ITSState *s, const uint64_t *cmdpkt, return update_ite(s, eventid, &dte, ite) ? CMD_CONTINUE : CMD_STALL; } -static bool update_cte(GICv3ITSState *s, uint16_t icid, bool valid, - uint64_t rdbase) +/* + * Update the Collection Table entry for @icid to @cte. Returns true + * on success, false if there was a memory access error. + */ +static bool update_cte(GICv3ITSState *s, uint16_t icid, const CTEntry *cte) { AddressSpace *as = &s->gicv3->dma_as; uint64_t entry_addr; - uint64_t cte = 0; + uint64_t cteval = 0; MemTxResult res = MEMTX_OK; if (!s->ct.valid) { return true; } - if (valid) { + if (cte->valid) { /* add mapping entry to collection table */ - cte = FIELD_DP64(cte, CTE, VALID, 1); - cte = FIELD_DP64(cte, CTE, RDBASE, rdbase); + cteval = FIELD_DP64(cteval, CTE, VALID, 1); + cteval = FIELD_DP64(cteval, CTE, RDBASE, cte->rdbase); } entry_addr = table_entry_addr(s, &s->ct, icid, &res); @@ -446,27 +449,26 @@ static bool update_cte(GICv3ITSState *s, uint16_t icid, bool valid, return true; } - address_space_stq_le(as, entry_addr, cte, MEMTXATTRS_UNSPECIFIED, &res); + address_space_stq_le(as, entry_addr, cteval, MEMTXATTRS_UNSPECIFIED, &res); return res == MEMTX_OK; } static ItsCmdResult process_mapc(GICv3ITSState *s, const uint64_t *cmdpkt) { uint16_t icid; - uint64_t rdbase; - bool valid; + CTEntry cte; icid = cmdpkt[2] & ICID_MASK; - rdbase = (cmdpkt[2] & R_MAPC_RDBASE_MASK) >> R_MAPC_RDBASE_SHIFT; - rdbase &= RDBASE_PROCNUM_MASK; + cte.rdbase = (cmdpkt[2] & R_MAPC_RDBASE_MASK) >> R_MAPC_RDBASE_SHIFT; + cte.rdbase &= RDBASE_PROCNUM_MASK; - valid = cmdpkt[2] & CMD_FIELD_VALID_MASK; + cte.valid = cmdpkt[2] & CMD_FIELD_VALID_MASK; - if ((icid >= s->ct.num_entries) || (rdbase >= s->gicv3->num_cpu)) { + if ((icid >= s->ct.num_entries) || (cte.rdbase >= s->gicv3->num_cpu)) { qemu_log_mask(LOG_GUEST_ERROR, "ITS MAPC: invalid collection table attributes " - "icid %d rdbase %" PRIu64 "\n", icid, rdbase); + "icid %d rdbase %u\n", icid, cte.rdbase); /* * in this implementation, in case of error * we ignore this command and move onto the next @@ -475,7 +477,7 @@ static ItsCmdResult process_mapc(GICv3ITSState *s, const uint64_t *cmdpkt) return CMD_CONTINUE; } - return update_cte(s, icid, valid, rdbase) ? CMD_CONTINUE : CMD_STALL; + return update_cte(s, icid, &cte) ? CMD_CONTINUE : CMD_STALL; } /* From a1ce993da6fc27b022075b02a2e1e1e5be329254 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 1 Feb 2022 19:32:00 +0000 Subject: [PATCH 286/460] hw/intc/arm_gicv3_its: Fix address calculation in get_ite() and update_ite() In get_ite() and update_ite() we work with a 12-byte in-guest-memory table entry, which we intend to handle as an 8-byte value followed by a 4-byte value. Unfortunately the calculation of the address of the 4-byte value is wrong, because we write it as: table_base_address + (index * entrysize) + 4 (obfuscated by the way the expression has been written) when it should be + 8. This bug meant that we overwrote the top bytes of the 8-byte value with the 4-byte value. There are no guest-visible effects because the top half of the 8-byte value contains only the doorbell interrupt field, which is used only in GICv4, and the two bugs in the "write ITE" and "read ITE" codepaths cancel each other out. We can't simply change the calculation, because this would break migration of a (TCG) guest from the old version of QEMU which had in-guest-memory interrupt tables written using the buggy version of update_ite(). We must also at the same time change the layout of the fields within the ITE_L and ITE_H values so that the in-memory locations of the fields we care about (VALID, INTTYPE, INTID and ICID) stay the same. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20220201193207.2771604-7-peter.maydell@linaro.org --- hw/intc/arm_gicv3_its.c | 28 +++++++++++----------------- hw/intc/gicv3_internal.h | 19 ++++++++++--------- 2 files changed, 21 insertions(+), 26 deletions(-) diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c index b94775fd37..48eaf20a6c 100644 --- a/hw/intc/arm_gicv3_its.c +++ b/hw/intc/arm_gicv3_its.c @@ -173,14 +173,12 @@ static bool update_ite(GICv3ITSState *s, uint32_t eventid, const DTEntry *dte, { AddressSpace *as = &s->gicv3->dma_as; MemTxResult res = MEMTX_OK; + hwaddr iteaddr = dte->ittaddr + eventid * ITS_ITT_ENTRY_SIZE; - address_space_stq_le(as, dte->ittaddr + (eventid * (sizeof(uint64_t) + - sizeof(uint32_t))), ite.itel, MEMTXATTRS_UNSPECIFIED, - &res); + address_space_stq_le(as, iteaddr, ite.itel, MEMTXATTRS_UNSPECIFIED, &res); if (res == MEMTX_OK) { - address_space_stl_le(as, dte->ittaddr + (eventid * (sizeof(uint64_t) + - sizeof(uint32_t))) + sizeof(uint32_t), ite.iteh, + address_space_stl_le(as, iteaddr + 8, ite.iteh, MEMTXATTRS_UNSPECIFIED, &res); } if (res != MEMTX_OK) { @@ -196,16 +194,12 @@ static bool get_ite(GICv3ITSState *s, uint32_t eventid, const DTEntry *dte, AddressSpace *as = &s->gicv3->dma_as; bool status = false; IteEntry ite = {}; + hwaddr iteaddr = dte->ittaddr + eventid * ITS_ITT_ENTRY_SIZE; - ite.itel = address_space_ldq_le(as, dte->ittaddr + - (eventid * (sizeof(uint64_t) + - sizeof(uint32_t))), MEMTXATTRS_UNSPECIFIED, - res); + ite.itel = address_space_ldq_le(as, iteaddr, MEMTXATTRS_UNSPECIFIED, res); if (*res == MEMTX_OK) { - ite.iteh = address_space_ldl_le(as, dte->ittaddr + - (eventid * (sizeof(uint64_t) + - sizeof(uint32_t))) + sizeof(uint32_t), + ite.iteh = address_space_ldl_le(as, iteaddr + 8, MEMTXATTRS_UNSPECIFIED, res); if (*res == MEMTX_OK) { @@ -213,7 +207,7 @@ static bool get_ite(GICv3ITSState *s, uint32_t eventid, const DTEntry *dte, int inttype = FIELD_EX64(ite.itel, ITE_L, INTTYPE); if (inttype == ITE_INTTYPE_PHYSICAL) { *pIntid = FIELD_EX64(ite.itel, ITE_L, INTID); - *icid = FIELD_EX32(ite.iteh, ITE_H, ICID); + *icid = FIELD_EX64(ite.itel, ITE_L, ICID); status = true; } } @@ -412,8 +406,8 @@ static ItsCmdResult process_mapti(GICv3ITSState *s, const uint64_t *cmdpkt, ite.itel = FIELD_DP64(ite.itel, ITE_L, VALID, true); ite.itel = FIELD_DP64(ite.itel, ITE_L, INTTYPE, ITE_INTTYPE_PHYSICAL); ite.itel = FIELD_DP64(ite.itel, ITE_L, INTID, pIntid); - ite.itel = FIELD_DP64(ite.itel, ITE_L, DOORBELL, INTID_SPURIOUS); - ite.iteh = FIELD_DP32(ite.iteh, ITE_H, ICID, icid); + ite.itel = FIELD_DP64(ite.itel, ITE_L, ICID, icid); + ite.iteh = FIELD_DP32(ite.iteh, ITE_H, DOORBELL, INTID_SPURIOUS); return update_ite(s, eventid, &dte, ite) ? CMD_CONTINUE : CMD_STALL; } @@ -688,8 +682,8 @@ static ItsCmdResult process_movi(GICv3ITSState *s, const uint64_t *cmdpkt) ite.itel = FIELD_DP64(ite.itel, ITE_L, VALID, 1); ite.itel = FIELD_DP64(ite.itel, ITE_L, INTTYPE, ITE_INTTYPE_PHYSICAL); ite.itel = FIELD_DP64(ite.itel, ITE_L, INTID, intid); - ite.itel = FIELD_DP64(ite.itel, ITE_L, DOORBELL, INTID_SPURIOUS); - ite.iteh = FIELD_DP32(ite.iteh, ITE_H, ICID, new_icid); + ite.itel = FIELD_DP64(ite.itel, ITE_L, ICID, new_icid); + ite.iteh = FIELD_DP32(ite.iteh, ITE_H, DOORBELL, INTID_SPURIOUS); return update_ite(s, eventid, &dte, ite) ? CMD_CONTINUE : CMD_STALL; } diff --git a/hw/intc/gicv3_internal.h b/hw/intc/gicv3_internal.h index 60c8617e4e..2bf1baef04 100644 --- a/hw/intc/gicv3_internal.h +++ b/hw/intc/gicv3_internal.h @@ -370,22 +370,23 @@ FIELD(MOVI_2, ICID, 0, 16) * 12 bytes Interrupt translation Table Entry size * as per Table 5.3 in GICv3 spec * ITE Lower 8 Bytes - * Bits: | 49 ... 26 | 25 ... 2 | 1 | 0 | - * Values: | Doorbell | IntNum | IntType | Valid | + * Bits: | 63 ... 48 | 47 ... 32 | 31 ... 26 | 25 ... 2 | 1 | 0 | + * Values: | vPEID | ICID | unused | IntNum | IntType | Valid | * ITE Higher 4 Bytes - * Bits: | 31 ... 16 | 15 ...0 | - * Values: | vPEID | ICID | - * (When Doorbell is unused, as it always is in GICv3, it is 1023) + * Bits: | 31 ... 25 | 24 ... 0 | + * Values: | unused | Doorbell | + * (When Doorbell is unused, as it always is for INTYPE_PHYSICAL, + * the value of that field in memory cannot be relied upon -- older + * versions of QEMU did not correctly write to that memory.) */ #define ITS_ITT_ENTRY_SIZE 0xC FIELD(ITE_L, VALID, 0, 1) FIELD(ITE_L, INTTYPE, 1, 1) FIELD(ITE_L, INTID, 2, 24) -FIELD(ITE_L, DOORBELL, 26, 24) - -FIELD(ITE_H, ICID, 0, 16) -FIELD(ITE_H, VPEID, 16, 16) +FIELD(ITE_L, ICID, 32, 16) +FIELD(ITE_L, VPEID, 48, 16) +FIELD(ITE_H, DOORBELL, 0, 24) /* Possible values for ITE_L INTTYPE */ #define ITE_INTTYPE_VIRTUAL 0 From 2954b93fe6e6173afbda124469af31c58e266ca1 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 1 Feb 2022 19:32:01 +0000 Subject: [PATCH 287/460] hw/intc/arm_gicv3_its: Avoid nested ifs in get_ite() The get_ite() code has some awkward nested if statements; clean them up by returning early if the memory accesses fail. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20220201193207.2771604-8-peter.maydell@linaro.org --- hw/intc/arm_gicv3_its.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c index 48eaf20a6c..6975a349f6 100644 --- a/hw/intc/arm_gicv3_its.c +++ b/hw/intc/arm_gicv3_its.c @@ -197,20 +197,22 @@ static bool get_ite(GICv3ITSState *s, uint32_t eventid, const DTEntry *dte, hwaddr iteaddr = dte->ittaddr + eventid * ITS_ITT_ENTRY_SIZE; ite.itel = address_space_ldq_le(as, iteaddr, MEMTXATTRS_UNSPECIFIED, res); + if (*res != MEMTX_OK) { + return false; + } - if (*res == MEMTX_OK) { - ite.iteh = address_space_ldl_le(as, iteaddr + 8, - MEMTXATTRS_UNSPECIFIED, res); + ite.iteh = address_space_ldl_le(as, iteaddr + 8, + MEMTXATTRS_UNSPECIFIED, res); + if (*res != MEMTX_OK) { + return false; + } - if (*res == MEMTX_OK) { - if (FIELD_EX64(ite.itel, ITE_L, VALID)) { - int inttype = FIELD_EX64(ite.itel, ITE_L, INTTYPE); - if (inttype == ITE_INTTYPE_PHYSICAL) { - *pIntid = FIELD_EX64(ite.itel, ITE_L, INTID); - *icid = FIELD_EX64(ite.itel, ITE_L, ICID); - status = true; - } - } + if (FIELD_EX64(ite.itel, ITE_L, VALID)) { + int inttype = FIELD_EX64(ite.itel, ITE_L, INTTYPE); + if (inttype == ITE_INTTYPE_PHYSICAL) { + *pIntid = FIELD_EX64(ite.itel, ITE_L, INTID); + *icid = FIELD_EX64(ite.itel, ITE_L, ICID); + status = true; } } return status; From 244194fe24dbaf1aa820a9ac5a9d7a8373288389 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 1 Feb 2022 19:32:02 +0000 Subject: [PATCH 288/460] hw/intc/arm_gicv3_its: Pass ITE values back from get_ite() via a struct In get_ite() we currently return the caller some of the fields of an Interrupt Table Entry via a set of pointer arguments, and validate some of them internally (interrupt type and valid bit) to return a simple true/false 'valid' indication. Define a new ITEntry struct which has all the fields that the in-memory ITE has, and bring the get_ite() function in to line with get_dte() and get_cte(). This paves the way for handling virtual interrupts, which will want a different subset of the fields in the ITE. Handling them under the old "lots of pointer arguments" scheme would have meant a confusingly large set of arguments for this function. The new struct ITEntry is obviously confusably similar to the existing IteEntry struct, whose fields are the raw 12 bytes of the in-memory ITE. In the next commit we will make update_ite() use ITEntry instead of IteEntry, which will allow us to delete the IteEntry struct and remove the confusion. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20220201193207.2771604-9-peter.maydell@linaro.org --- hw/intc/arm_gicv3_its.c | 102 ++++++++++++++++++++++------------------ 1 file changed, 55 insertions(+), 47 deletions(-) diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c index 6975a349f6..bd74108502 100644 --- a/hw/intc/arm_gicv3_its.c +++ b/hw/intc/arm_gicv3_its.c @@ -57,6 +57,16 @@ typedef struct CTEntry { uint32_t rdbase; } CTEntry; +typedef struct ITEntry { + bool valid; + int inttype; + uint32_t intid; + uint32_t doorbell; + uint32_t icid; + uint32_t vpeid; +} ITEntry; + + /* * The ITS spec permits a range of CONSTRAINED UNPREDICTABLE options * if a command parameter is not correct. These include both "stall @@ -188,34 +198,38 @@ static bool update_ite(GICv3ITSState *s, uint32_t eventid, const DTEntry *dte, } } -static bool get_ite(GICv3ITSState *s, uint32_t eventid, const DTEntry *dte, - uint16_t *icid, uint32_t *pIntid, MemTxResult *res) +/* + * Read the Interrupt Table entry at index @eventid from the table specified + * by the DTE @dte. On success, we return MEMTX_OK and populate the ITEntry + * struct @ite accordingly. If there is an error reading memory then we return + * the error code. + */ +static MemTxResult get_ite(GICv3ITSState *s, uint32_t eventid, + const DTEntry *dte, ITEntry *ite) { AddressSpace *as = &s->gicv3->dma_as; - bool status = false; - IteEntry ite = {}; + MemTxResult res = MEMTX_OK; + uint64_t itel; + uint32_t iteh; hwaddr iteaddr = dte->ittaddr + eventid * ITS_ITT_ENTRY_SIZE; - ite.itel = address_space_ldq_le(as, iteaddr, MEMTXATTRS_UNSPECIFIED, res); - if (*res != MEMTX_OK) { - return false; + itel = address_space_ldq_le(as, iteaddr, MEMTXATTRS_UNSPECIFIED, &res); + if (res != MEMTX_OK) { + return res; } - ite.iteh = address_space_ldl_le(as, iteaddr + 8, - MEMTXATTRS_UNSPECIFIED, res); - if (*res != MEMTX_OK) { - return false; + iteh = address_space_ldl_le(as, iteaddr + 8, MEMTXATTRS_UNSPECIFIED, &res); + if (res != MEMTX_OK) { + return res; } - if (FIELD_EX64(ite.itel, ITE_L, VALID)) { - int inttype = FIELD_EX64(ite.itel, ITE_L, INTTYPE); - if (inttype == ITE_INTTYPE_PHYSICAL) { - *pIntid = FIELD_EX64(ite.itel, ITE_L, INTID); - *icid = FIELD_EX64(ite.itel, ITE_L, ICID); - status = true; - } - } - return status; + ite->valid = FIELD_EX64(itel, ITE_L, VALID); + ite->inttype = FIELD_EX64(itel, ITE_L, INTTYPE); + ite->intid = FIELD_EX64(itel, ITE_L, INTID); + ite->icid = FIELD_EX64(itel, ITE_L, ICID); + ite->vpeid = FIELD_EX64(itel, ITE_L, VPEID); + ite->doorbell = FIELD_EX64(iteh, ITE_H, DOORBELL); + return MEMTX_OK; } /* @@ -258,13 +272,10 @@ static MemTxResult get_dte(GICv3ITSState *s, uint32_t devid, DTEntry *dte) static ItsCmdResult do_process_its_cmd(GICv3ITSState *s, uint32_t devid, uint32_t eventid, ItsCmdType cmd) { - MemTxResult res = MEMTX_OK; uint64_t num_eventids; - uint16_t icid = 0; - uint32_t pIntid = 0; - bool ite_valid = false; DTEntry dte; CTEntry cte; + ITEntry ite; if (devid >= s->dt.num_entries) { qemu_log_mask(LOG_GUEST_ERROR, @@ -292,26 +303,25 @@ static ItsCmdResult do_process_its_cmd(GICv3ITSState *s, uint32_t devid, return CMD_CONTINUE; } - ite_valid = get_ite(s, eventid, &dte, &icid, &pIntid, &res); - if (res != MEMTX_OK) { + if (get_ite(s, eventid, &dte, &ite) != MEMTX_OK) { return CMD_STALL; } - if (!ite_valid) { + if (!ite.valid || ite.inttype != ITE_INTTYPE_PHYSICAL) { qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid command attributes: invalid ITE\n", __func__); return CMD_CONTINUE; } - if (icid >= s->ct.num_entries) { + if (ite.icid >= s->ct.num_entries) { qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid ICID 0x%x in ITE (table corrupted?)\n", - __func__, icid); + __func__, ite.icid); return CMD_CONTINUE; } - if (get_cte(s, icid, &cte) != MEMTX_OK) { + if (get_cte(s, ite.icid, &cte) != MEMTX_OK) { return CMD_STALL; } if (!cte.valid) { @@ -330,15 +340,15 @@ static ItsCmdResult do_process_its_cmd(GICv3ITSState *s, uint32_t devid, } if ((cmd == CLEAR) || (cmd == DISCARD)) { - gicv3_redist_process_lpi(&s->gicv3->cpu[cte.rdbase], pIntid, 0); + gicv3_redist_process_lpi(&s->gicv3->cpu[cte.rdbase], ite.intid, 0); } else { - gicv3_redist_process_lpi(&s->gicv3->cpu[cte.rdbase], pIntid, 1); + gicv3_redist_process_lpi(&s->gicv3->cpu[cte.rdbase], ite.intid, 1); } if (cmd == DISCARD) { - IteEntry ite = {}; + IteEntry itee = {}; /* remove mapping from interrupt translation table */ - return update_ite(s, eventid, &dte, ite) ? CMD_CONTINUE : CMD_STALL; + return update_ite(s, eventid, &dte, itee) ? CMD_CONTINUE : CMD_STALL; } return CMD_CONTINUE; } @@ -572,14 +582,13 @@ static ItsCmdResult process_movall(GICv3ITSState *s, const uint64_t *cmdpkt) static ItsCmdResult process_movi(GICv3ITSState *s, const uint64_t *cmdpkt) { - MemTxResult res = MEMTX_OK; - uint32_t devid, eventid, intid; - uint16_t old_icid, new_icid; - bool ite_valid; + uint32_t devid, eventid; + uint16_t new_icid; uint64_t num_eventids; IteEntry ite = {}; DTEntry dte; CTEntry old_cte, new_cte; + ITEntry old_ite; devid = FIELD_EX64(cmdpkt[0], MOVI_0, DEVICEID); eventid = FIELD_EX64(cmdpkt[1], MOVI_1, EVENTID); @@ -611,22 +620,21 @@ static ItsCmdResult process_movi(GICv3ITSState *s, const uint64_t *cmdpkt) return CMD_CONTINUE; } - ite_valid = get_ite(s, eventid, &dte, &old_icid, &intid, &res); - if (res != MEMTX_OK) { + if (get_ite(s, eventid, &dte, &old_ite) != MEMTX_OK) { return CMD_STALL; } - if (!ite_valid) { + if (!old_ite.valid || old_ite.inttype != ITE_INTTYPE_PHYSICAL) { qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid command attributes: invalid ITE\n", __func__); return CMD_CONTINUE; } - if (old_icid >= s->ct.num_entries) { + if (old_ite.icid >= s->ct.num_entries) { qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid ICID 0x%x in ITE (table corrupted?)\n", - __func__, old_icid); + __func__, old_ite.icid); return CMD_CONTINUE; } @@ -637,14 +645,14 @@ static ItsCmdResult process_movi(GICv3ITSState *s, const uint64_t *cmdpkt) return CMD_CONTINUE; } - if (get_cte(s, old_icid, &old_cte) != MEMTX_OK) { + if (get_cte(s, old_ite.icid, &old_cte) != MEMTX_OK) { return CMD_STALL; } if (!old_cte.valid) { qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid command attributes: " "invalid CTE for old ICID 0x%x\n", - __func__, old_icid); + __func__, old_ite.icid); return CMD_CONTINUE; } @@ -677,13 +685,13 @@ static ItsCmdResult process_movi(GICv3ITSState *s, const uint64_t *cmdpkt) /* Move the LPI from the old redistributor to the new one */ gicv3_redist_mov_lpi(&s->gicv3->cpu[old_cte.rdbase], &s->gicv3->cpu[new_cte.rdbase], - intid); + old_ite.intid); } /* Update the ICID field in the interrupt translation table entry */ ite.itel = FIELD_DP64(ite.itel, ITE_L, VALID, 1); ite.itel = FIELD_DP64(ite.itel, ITE_L, INTTYPE, ITE_INTTYPE_PHYSICAL); - ite.itel = FIELD_DP64(ite.itel, ITE_L, INTID, intid); + ite.itel = FIELD_DP64(ite.itel, ITE_L, INTID, old_ite.intid); ite.itel = FIELD_DP64(ite.itel, ITE_L, ICID, new_icid); ite.iteh = FIELD_DP32(ite.iteh, ITE_H, DOORBELL, INTID_SPURIOUS); return update_ite(s, eventid, &dte, ite) ? CMD_CONTINUE : CMD_STALL; From 7eb54267f243a336deaf3c806a5b5422365ee861 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 1 Feb 2022 19:32:03 +0000 Subject: [PATCH 289/460] hw/intc/arm_gicv3_its: Make update_ite() use ITEntry Make the update_ite() struct use the new ITEntry struct, so that callers don't need to assemble the in-memory ITE data themselves, and only get_ite() and update_ite() need to care about that in-memory layout. We can then drop the no-longer-used IteEntry struct definition. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20220201193207.2771604-10-peter.maydell@linaro.org --- hw/intc/arm_gicv3_its.c | 62 +++++++++++++++++++++-------------------- 1 file changed, 32 insertions(+), 30 deletions(-) diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c index bd74108502..e3b63efddc 100644 --- a/hw/intc/arm_gicv3_its.c +++ b/hw/intc/arm_gicv3_its.c @@ -41,11 +41,6 @@ typedef enum ItsCmdType { INTERRUPT = 3, } ItsCmdType; -typedef struct { - uint32_t iteh; - uint64_t itel; -} IteEntry; - typedef struct DTEntry { bool valid; unsigned size; @@ -178,24 +173,35 @@ static MemTxResult get_cte(GICv3ITSState *s, uint16_t icid, CTEntry *cte) return MEMTX_OK; } +/* + * Update the Interrupt Table entry at index @evinted in the table specified + * by the dte @dte. Returns true on success, false if there was a memory + * access error. + */ static bool update_ite(GICv3ITSState *s, uint32_t eventid, const DTEntry *dte, - IteEntry ite) + const ITEntry *ite) { AddressSpace *as = &s->gicv3->dma_as; MemTxResult res = MEMTX_OK; hwaddr iteaddr = dte->ittaddr + eventid * ITS_ITT_ENTRY_SIZE; + uint64_t itel = 0; + uint32_t iteh = 0; - address_space_stq_le(as, iteaddr, ite.itel, MEMTXATTRS_UNSPECIFIED, &res); - - if (res == MEMTX_OK) { - address_space_stl_le(as, iteaddr + 8, ite.iteh, - MEMTXATTRS_UNSPECIFIED, &res); + if (ite->valid) { + itel = FIELD_DP64(itel, ITE_L, VALID, 1); + itel = FIELD_DP64(itel, ITE_L, INTTYPE, ite->inttype); + itel = FIELD_DP64(itel, ITE_L, INTID, ite->intid); + itel = FIELD_DP64(itel, ITE_L, ICID, ite->icid); + itel = FIELD_DP64(itel, ITE_L, VPEID, ite->vpeid); + iteh = FIELD_DP32(iteh, ITE_H, DOORBELL, ite->doorbell); } + + address_space_stq_le(as, iteaddr, itel, MEMTXATTRS_UNSPECIFIED, &res); if (res != MEMTX_OK) { return false; - } else { - return true; } + address_space_stl_le(as, iteaddr + 8, iteh, MEMTXATTRS_UNSPECIFIED, &res); + return res == MEMTX_OK; } /* @@ -346,9 +352,10 @@ static ItsCmdResult do_process_its_cmd(GICv3ITSState *s, uint32_t devid, } if (cmd == DISCARD) { - IteEntry itee = {}; + ITEntry ite = {}; /* remove mapping from interrupt translation table */ - return update_ite(s, eventid, &dte, itee) ? CMD_CONTINUE : CMD_STALL; + ite.valid = false; + return update_ite(s, eventid, &dte, &ite) ? CMD_CONTINUE : CMD_STALL; } return CMD_CONTINUE; } @@ -370,8 +377,8 @@ static ItsCmdResult process_mapti(GICv3ITSState *s, const uint64_t *cmdpkt, uint64_t num_eventids; uint32_t num_intids; uint16_t icid = 0; - IteEntry ite = {}; DTEntry dte; + ITEntry ite; devid = (cmdpkt[0] & DEVID_MASK) >> DEVID_SHIFT; eventid = cmdpkt[1] & EVENTID_MASK; @@ -415,13 +422,13 @@ static ItsCmdResult process_mapti(GICv3ITSState *s, const uint64_t *cmdpkt, } /* add ite entry to interrupt translation table */ - ite.itel = FIELD_DP64(ite.itel, ITE_L, VALID, true); - ite.itel = FIELD_DP64(ite.itel, ITE_L, INTTYPE, ITE_INTTYPE_PHYSICAL); - ite.itel = FIELD_DP64(ite.itel, ITE_L, INTID, pIntid); - ite.itel = FIELD_DP64(ite.itel, ITE_L, ICID, icid); - ite.iteh = FIELD_DP32(ite.iteh, ITE_H, DOORBELL, INTID_SPURIOUS); - - return update_ite(s, eventid, &dte, ite) ? CMD_CONTINUE : CMD_STALL; + ite.valid = true; + ite.inttype = ITE_INTTYPE_PHYSICAL; + ite.intid = pIntid; + ite.icid = icid; + ite.doorbell = INTID_SPURIOUS; + ite.vpeid = 0; + return update_ite(s, eventid, &dte, &ite) ? CMD_CONTINUE : CMD_STALL; } /* @@ -585,7 +592,6 @@ static ItsCmdResult process_movi(GICv3ITSState *s, const uint64_t *cmdpkt) uint32_t devid, eventid; uint16_t new_icid; uint64_t num_eventids; - IteEntry ite = {}; DTEntry dte; CTEntry old_cte, new_cte; ITEntry old_ite; @@ -689,12 +695,8 @@ static ItsCmdResult process_movi(GICv3ITSState *s, const uint64_t *cmdpkt) } /* Update the ICID field in the interrupt translation table entry */ - ite.itel = FIELD_DP64(ite.itel, ITE_L, VALID, 1); - ite.itel = FIELD_DP64(ite.itel, ITE_L, INTTYPE, ITE_INTTYPE_PHYSICAL); - ite.itel = FIELD_DP64(ite.itel, ITE_L, INTID, old_ite.intid); - ite.itel = FIELD_DP64(ite.itel, ITE_L, ICID, new_icid); - ite.iteh = FIELD_DP32(ite.iteh, ITE_H, DOORBELL, INTID_SPURIOUS); - return update_ite(s, eventid, &dte, ite) ? CMD_CONTINUE : CMD_STALL; + old_ite.icid = new_icid; + return update_ite(s, eventid, &dte, &old_ite) ? CMD_CONTINUE : CMD_STALL; } /* From da4680ce3a03b0cc13fe7a2b98b815c039517f26 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 1 Feb 2022 19:32:04 +0000 Subject: [PATCH 290/460] hw/intc/arm_gicv3_its: Drop TableDesc and CmdQDesc valid fields Currently we track in the TableDesc and CmdQDesc structs the state of the GITS_BASER and GITS_CBASER Valid bits. However we aren't very consistent abut checking the valid field: we test it in update_cte() and update_dte(), but not anywhere else we look things up in tables. The GIC specification says that it is UNPREDICTABLE if a guest fails to set any of these Valid bits before enabling the ITS via GITS_CTLR.Enabled. So we can choose to handle Valid == 0 as equivalent to a zero-length table. This is in fact how we're already catching this case in most of the table-access paths: when Valid is 0 we leave the num_entries fields in TableDesc or CmdQDesc set to zero, and then the out-of-bounds check "index >= num_entries" that we have to do anyway before doing any of these table lookups will always be true, catching the no-valid-table case without any extra code. So we can remove the checks on the valid field from update_cte() and update_dte(): since these happen after the bounds check there was never any case when the test could fail. That means the valid fields would be entirely unused, so just remove them. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20220201193207.2771604-11-peter.maydell@linaro.org --- hw/intc/arm_gicv3_its.c | 31 ++++++++++++-------------- include/hw/intc/arm_gicv3_its_common.h | 2 -- 2 files changed, 14 insertions(+), 19 deletions(-) diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c index e3b63efddc..9735d609df 100644 --- a/hw/intc/arm_gicv3_its.c +++ b/hw/intc/arm_gicv3_its.c @@ -442,10 +442,6 @@ static bool update_cte(GICv3ITSState *s, uint16_t icid, const CTEntry *cte) uint64_t cteval = 0; MemTxResult res = MEMTX_OK; - if (!s->ct.valid) { - return true; - } - if (cte->valid) { /* add mapping entry to collection table */ cteval = FIELD_DP64(cteval, CTE, VALID, 1); @@ -504,15 +500,11 @@ static bool update_dte(GICv3ITSState *s, uint32_t devid, const DTEntry *dte) uint64_t dteval = 0; MemTxResult res = MEMTX_OK; - if (s->dt.valid) { - if (dte->valid) { - /* add mapping entry to device table */ - dteval = FIELD_DP64(dteval, DTE, VALID, 1); - dteval = FIELD_DP64(dteval, DTE, SIZE, dte->size); - dteval = FIELD_DP64(dteval, DTE, ITTADDR, dte->ittaddr); - } - } else { - return true; + if (dte->valid) { + /* add mapping entry to device table */ + dteval = FIELD_DP64(dteval, DTE, VALID, 1); + dteval = FIELD_DP64(dteval, DTE, SIZE, dte->size); + dteval = FIELD_DP64(dteval, DTE, ITTADDR, dte->ittaddr); } entry_addr = table_entry_addr(s, &s->dt, devid, &res); @@ -901,7 +893,6 @@ static void extract_table_params(GICv3ITSState *s) } memset(td, 0, sizeof(*td)); - td->valid = FIELD_EX64(value, GITS_BASER, VALID); /* * If GITS_BASER.Valid is 0 for any then we will not process * interrupts. (GITS_TYPER.HCC is 0 for this implementation, so we @@ -909,8 +900,15 @@ static void extract_table_params(GICv3ITSState *s) * for the register corresponding to the Collection table but we * still have to process interrupts using non-memory-backed * Collection table entries.) + * The specification makes it UNPREDICTABLE to enable the ITS without + * marking each BASER as valid. We choose to handle these as if + * the table was zero-sized, so commands using the table will fail + * and interrupts requested via GITS_TRANSLATER writes will be ignored. + * This happens automatically by leaving the num_entries field at + * zero, which will be caught by the bounds checks we have before + * every table lookup anyway. */ - if (!td->valid) { + if (!FIELD_EX64(value, GITS_BASER, VALID)) { continue; } td->page_sz = page_sz; @@ -936,9 +934,8 @@ static void extract_cmdq_params(GICv3ITSState *s) num_pages = FIELD_EX64(value, GITS_CBASER, SIZE) + 1; memset(&s->cq, 0 , sizeof(s->cq)); - s->cq.valid = FIELD_EX64(value, GITS_CBASER, VALID); - if (s->cq.valid) { + if (FIELD_EX64(value, GITS_CBASER, VALID)) { s->cq.num_entries = (num_pages * GITS_PAGE_SIZE_4K) / GITS_CMDQ_ENTRY_SIZE; s->cq.base_addr = FIELD_EX64(value, GITS_CBASER, PHYADDR); diff --git a/include/hw/intc/arm_gicv3_its_common.h b/include/hw/intc/arm_gicv3_its_common.h index 3e2ad2dff6..0f130494dd 100644 --- a/include/hw/intc/arm_gicv3_its_common.h +++ b/include/hw/intc/arm_gicv3_its_common.h @@ -42,7 +42,6 @@ #define GITS_TRANSLATER 0x0040 typedef struct { - bool valid; bool indirect; uint16_t entry_sz; uint32_t page_sz; @@ -51,7 +50,6 @@ typedef struct { } TableDesc; typedef struct { - bool valid; uint32_t num_entries; uint64_t base_addr; } CmdQDesc; From 84d43d2e82dad29db43a96c2ef22606ce834b248 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 1 Feb 2022 19:32:05 +0000 Subject: [PATCH 291/460] hw/intc/arm_gicv3_its: In MAPC with V=0, don't check rdbase field In the MAPC command, if V=0 this is a request to delete a collection table entry and the rdbase field of the command packet will not be used. In particular, the specification says that the "UNPREDICTABLE if rdbase is not valid" only applies for V=1. We were doing a check-and-log-guest-error on rdbase regardless of whether the V bit was set, and also (harmlessly but confusingly) storing the contents of the rdbase field into the updated collection table entry. Update the code so that if V=0 we don't check or use the rdbase field value. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20220201193207.2771604-12-peter.maydell@linaro.org --- hw/intc/arm_gicv3_its.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c index 9735d609df..069991f7f3 100644 --- a/hw/intc/arm_gicv3_its.c +++ b/hw/intc/arm_gicv3_its.c @@ -468,21 +468,21 @@ static ItsCmdResult process_mapc(GICv3ITSState *s, const uint64_t *cmdpkt) CTEntry cte; icid = cmdpkt[2] & ICID_MASK; - - cte.rdbase = (cmdpkt[2] & R_MAPC_RDBASE_MASK) >> R_MAPC_RDBASE_SHIFT; - cte.rdbase &= RDBASE_PROCNUM_MASK; - cte.valid = cmdpkt[2] & CMD_FIELD_VALID_MASK; + if (cte.valid) { + cte.rdbase = (cmdpkt[2] & R_MAPC_RDBASE_MASK) >> R_MAPC_RDBASE_SHIFT; + cte.rdbase &= RDBASE_PROCNUM_MASK; + } else { + cte.rdbase = 0; + } - if ((icid >= s->ct.num_entries) || (cte.rdbase >= s->gicv3->num_cpu)) { + if (icid >= s->ct.num_entries) { + qemu_log_mask(LOG_GUEST_ERROR, "ITS MAPC: invalid ICID 0x%d", icid); + return CMD_CONTINUE; + } + if (cte.valid && cte.rdbase >= s->gicv3->num_cpu) { qemu_log_mask(LOG_GUEST_ERROR, - "ITS MAPC: invalid collection table attributes " - "icid %d rdbase %u\n", icid, cte.rdbase); - /* - * in this implementation, in case of error - * we ignore this command and move onto the next - * command in the queue - */ + "ITS MAPC: invalid RDBASE %u ", cte.rdbase); return CMD_CONTINUE; } From 333024140730c9294dafb4158e396faeb4b3f6fe Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 1 Feb 2022 19:32:06 +0000 Subject: [PATCH 292/460] hw/intc/arm_gicv3_its: Don't allow intid 1023 in MAPI/MAPTI When handling MAPI/MAPTI, we allow the supplied interrupt ID to be either 1023 or something in the valid LPI range. This is a mistake: only a real valid LPI is allowed. (The general behaviour of the ITS is that most interrupt ID fields require a value in the LPI range; the exception is that fields specifying a doorbell value, which are all in GICv4 commands, allow also 1023 to mean "no doorbell".) Remove the condition that incorrectly allows 1023 here. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20220201193207.2771604-13-peter.maydell@linaro.org --- hw/intc/arm_gicv3_its.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c index 069991f7f3..8dade9440a 100644 --- a/hw/intc/arm_gicv3_its.c +++ b/hw/intc/arm_gicv3_its.c @@ -406,8 +406,7 @@ static ItsCmdResult process_mapti(GICv3ITSState *s, const uint64_t *cmdpkt, if ((icid >= s->ct.num_entries) || !dte.valid || (eventid >= num_eventids) || - (((pIntid < GICV3_LPI_INTID_START) || (pIntid >= num_intids)) && - (pIntid != INTID_SPURIOUS))) { + (((pIntid < GICV3_LPI_INTID_START) || (pIntid >= num_intids)))) { qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid command attributes " "icid %d or eventid %d or pIntid %d or" From d7d359c4ac1cafd1051c5f6f7ff8349aba579718 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 1 Feb 2022 19:32:07 +0000 Subject: [PATCH 293/460] hw/intc/arm_gicv3_its: Split error checks In most of the ITS command processing, we check different error possibilities one at a time and log them appropriately. In process_mapti() and process_mapd() we have code which checks multiple error cases at once, which means the logging is less specific than it could be. Split those cases up. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20220201193207.2771604-14-peter.maydell@linaro.org --- hw/intc/arm_gicv3_its.c | 52 ++++++++++++++++++++++++----------------- 1 file changed, 31 insertions(+), 21 deletions(-) diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c index 8dade9440a..4f598d3c14 100644 --- a/hw/intc/arm_gicv3_its.c +++ b/hw/intc/arm_gicv3_its.c @@ -404,19 +404,29 @@ static ItsCmdResult process_mapti(GICv3ITSState *s, const uint64_t *cmdpkt, num_eventids = 1ULL << (dte.size + 1); num_intids = 1ULL << (GICD_TYPER_IDBITS + 1); - if ((icid >= s->ct.num_entries) - || !dte.valid || (eventid >= num_eventids) || - (((pIntid < GICV3_LPI_INTID_START) || (pIntid >= num_intids)))) { + if (icid >= s->ct.num_entries) { qemu_log_mask(LOG_GUEST_ERROR, - "%s: invalid command attributes " - "icid %d or eventid %d or pIntid %d or" - "unmapped dte %d\n", __func__, icid, eventid, - pIntid, dte.valid); - /* - * in this implementation, in case of error - * we ignore this command and move onto the next - * command in the queue - */ + "%s: invalid ICID 0x%x >= 0x%x\n", + __func__, icid, s->ct.num_entries); + return CMD_CONTINUE; + } + + if (!dte.valid) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: no valid DTE for devid 0x%x\n", __func__, devid); + return CMD_CONTINUE; + } + + if (eventid >= num_eventids) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: invalid event ID 0x%x >= 0x%" PRIx64 "\n", + __func__, eventid, num_eventids); + return CMD_CONTINUE; + } + + if (pIntid < GICV3_LPI_INTID_START || pIntid >= num_intids) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: invalid interrupt ID 0x%x\n", __func__, pIntid); return CMD_CONTINUE; } @@ -529,16 +539,16 @@ static ItsCmdResult process_mapd(GICv3ITSState *s, const uint64_t *cmdpkt) dte.ittaddr = (cmdpkt[2] & ITTADDR_MASK) >> ITTADDR_SHIFT; dte.valid = cmdpkt[2] & CMD_FIELD_VALID_MASK; - if ((devid >= s->dt.num_entries) || - (dte.size > FIELD_EX64(s->typer, GITS_TYPER, IDBITS))) { + if (devid >= s->dt.num_entries) { qemu_log_mask(LOG_GUEST_ERROR, - "ITS MAPD: invalid device table attributes " - "devid %d or size %d\n", devid, dte.size); - /* - * in this implementation, in case of error - * we ignore this command and move onto the next - * command in the queue - */ + "ITS MAPD: invalid device ID field 0x%x >= 0x%x\n", + devid, s->dt.num_entries); + return CMD_CONTINUE; + } + + if (dte.size > FIELD_EX64(s->typer, GITS_TYPER, IDBITS)) { + qemu_log_mask(LOG_GUEST_ERROR, + "ITS MAPD: invalid size %d\n", dte.size); return CMD_CONTINUE; } From 4fd1ebb10593087d45d2f56f7f3d13447d24802c Mon Sep 17 00:00:00 2001 From: Kevin Townsend Date: Sun, 30 Jan 2022 10:50:32 +0100 Subject: [PATCH 294/460] hw/sensor: Add lsm303dlhc magnetometer device This commit adds emulation of the magnetometer on the LSM303DLHC. It allows the magnetometer's X, Y and Z outputs to be set via the mag-x, mag-y and mag-z properties, as well as the 12-bit temperature output via the temperature property. Sensor can be enabled with 'CONFIG_LSM303DLHC_MAG=y'. Signed-off-by: Kevin Townsend Message-id: 20220130095032.35392-1-kevin.townsend@linaro.org Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/sensor/Kconfig | 4 + hw/sensor/lsm303dlhc_mag.c | 556 ++++++++++++++++++++++++++++++ hw/sensor/meson.build | 1 + tests/qtest/lsm303dlhc-mag-test.c | 148 ++++++++ tests/qtest/meson.build | 1 + 5 files changed, 710 insertions(+) create mode 100644 hw/sensor/lsm303dlhc_mag.c create mode 100644 tests/qtest/lsm303dlhc-mag-test.c diff --git a/hw/sensor/Kconfig b/hw/sensor/Kconfig index 9c8a049b06..b317f91b7b 100644 --- a/hw/sensor/Kconfig +++ b/hw/sensor/Kconfig @@ -21,3 +21,7 @@ config ADM1272 config MAX34451 bool depends on I2C + +config LSM303DLHC_MAG + bool + depends on I2C diff --git a/hw/sensor/lsm303dlhc_mag.c b/hw/sensor/lsm303dlhc_mag.c new file mode 100644 index 0000000000..4c98ddbf20 --- /dev/null +++ b/hw/sensor/lsm303dlhc_mag.c @@ -0,0 +1,556 @@ +/* + * LSM303DLHC I2C magnetometer. + * + * Copyright (C) 2021 Linaro Ltd. + * Written by Kevin Townsend + * + * Based on: https://www.st.com/resource/en/datasheet/lsm303dlhc.pdf + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +/* + * The I2C address associated with this device is set on the command-line when + * initialising the machine, but the following address is standard: 0x1E. + * + * Get and set functions for 'mag-x', 'mag-y' and 'mag-z' assume that + * 1 = 0.001 uT. (NOTE the 1 gauss = 100 uT, so setting a value of 100,000 + * would be equal to 1 gauss or 100 uT.) + * + * Get and set functions for 'temperature' assume that 1 = 0.001 C, so 23.6 C + * would be equal to 23600. + */ + +#include "qemu/osdep.h" +#include "hw/i2c/i2c.h" +#include "migration/vmstate.h" +#include "qapi/error.h" +#include "qapi/visitor.h" +#include "qemu/module.h" +#include "qemu/log.h" +#include "qemu/bswap.h" + +enum LSM303DLHCMagReg { + LSM303DLHC_MAG_REG_CRA = 0x00, + LSM303DLHC_MAG_REG_CRB = 0x01, + LSM303DLHC_MAG_REG_MR = 0x02, + LSM303DLHC_MAG_REG_OUT_X_H = 0x03, + LSM303DLHC_MAG_REG_OUT_X_L = 0x04, + LSM303DLHC_MAG_REG_OUT_Z_H = 0x05, + LSM303DLHC_MAG_REG_OUT_Z_L = 0x06, + LSM303DLHC_MAG_REG_OUT_Y_H = 0x07, + LSM303DLHC_MAG_REG_OUT_Y_L = 0x08, + LSM303DLHC_MAG_REG_SR = 0x09, + LSM303DLHC_MAG_REG_IRA = 0x0A, + LSM303DLHC_MAG_REG_IRB = 0x0B, + LSM303DLHC_MAG_REG_IRC = 0x0C, + LSM303DLHC_MAG_REG_TEMP_OUT_H = 0x31, + LSM303DLHC_MAG_REG_TEMP_OUT_L = 0x32 +}; + +typedef struct LSM303DLHCMagState { + I2CSlave parent_obj; + uint8_t cra; + uint8_t crb; + uint8_t mr; + int16_t x; + int16_t z; + int16_t y; + int16_t x_lock; + int16_t z_lock; + int16_t y_lock; + uint8_t sr; + uint8_t ira; + uint8_t irb; + uint8_t irc; + int16_t temperature; + int16_t temperature_lock; + uint8_t len; + uint8_t buf; + uint8_t pointer; +} LSM303DLHCMagState; + +#define TYPE_LSM303DLHC_MAG "lsm303dlhc_mag" +OBJECT_DECLARE_SIMPLE_TYPE(LSM303DLHCMagState, LSM303DLHC_MAG) + +/* + * Conversion factor from Gauss to sensor values for each GN gain setting, + * in units "lsb per Gauss" (see data sheet table 3). There is no documented + * behaviour if the GN setting in CRB is incorrectly set to 0b000; + * we arbitrarily make it the same as 0b001. + */ +uint32_t xy_gain[] = { 1100, 1100, 855, 670, 450, 400, 330, 230 }; +uint32_t z_gain[] = { 980, 980, 760, 600, 400, 355, 295, 205 }; + +static void lsm303dlhc_mag_get_x(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + LSM303DLHCMagState *s = LSM303DLHC_MAG(obj); + int gm = extract32(s->crb, 5, 3); + + /* Convert to uT where 1000 = 1 uT. Conversion factor depends on gain. */ + int64_t value = muldiv64(s->x, 100000, xy_gain[gm]); + visit_type_int(v, name, &value, errp); +} + +static void lsm303dlhc_mag_get_y(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + LSM303DLHCMagState *s = LSM303DLHC_MAG(obj); + int gm = extract32(s->crb, 5, 3); + + /* Convert to uT where 1000 = 1 uT. Conversion factor depends on gain. */ + int64_t value = muldiv64(s->y, 100000, xy_gain[gm]); + visit_type_int(v, name, &value, errp); +} + +static void lsm303dlhc_mag_get_z(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + LSM303DLHCMagState *s = LSM303DLHC_MAG(obj); + int gm = extract32(s->crb, 5, 3); + + /* Convert to uT where 1000 = 1 uT. Conversion factor depends on gain. */ + int64_t value = muldiv64(s->z, 100000, z_gain[gm]); + visit_type_int(v, name, &value, errp); +} + +static void lsm303dlhc_mag_set_x(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + LSM303DLHCMagState *s = LSM303DLHC_MAG(obj); + int64_t value; + int64_t reg; + int gm = extract32(s->crb, 5, 3); + + if (!visit_type_int(v, name, &value, errp)) { + return; + } + + reg = muldiv64(value, xy_gain[gm], 100000); + + /* Make sure we are within a 12-bit limit. */ + if (reg > 2047 || reg < -2048) { + error_setg(errp, "value %" PRId64 " out of register's range", value); + return; + } + + s->x = (int16_t)reg; +} + +static void lsm303dlhc_mag_set_y(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + LSM303DLHCMagState *s = LSM303DLHC_MAG(obj); + int64_t value; + int64_t reg; + int gm = extract32(s->crb, 5, 3); + + if (!visit_type_int(v, name, &value, errp)) { + return; + } + + reg = muldiv64(value, xy_gain[gm], 100000); + + /* Make sure we are within a 12-bit limit. */ + if (reg > 2047 || reg < -2048) { + error_setg(errp, "value %" PRId64 " out of register's range", value); + return; + } + + s->y = (int16_t)reg; +} + +static void lsm303dlhc_mag_set_z(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + LSM303DLHCMagState *s = LSM303DLHC_MAG(obj); + int64_t value; + int64_t reg; + int gm = extract32(s->crb, 5, 3); + + if (!visit_type_int(v, name, &value, errp)) { + return; + } + + reg = muldiv64(value, z_gain[gm], 100000); + + /* Make sure we are within a 12-bit limit. */ + if (reg > 2047 || reg < -2048) { + error_setg(errp, "value %" PRId64 " out of register's range", value); + return; + } + + s->z = (int16_t)reg; +} + +/* + * Get handler for the temperature property. + */ +static void lsm303dlhc_mag_get_temperature(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + LSM303DLHCMagState *s = LSM303DLHC_MAG(obj); + int64_t value; + + /* Convert to 1 lsb = 0.125 C to 1 = 0.001 C for 'temperature' property. */ + value = s->temperature * 125; + + visit_type_int(v, name, &value, errp); +} + +/* + * Set handler for the temperature property. + */ +static void lsm303dlhc_mag_set_temperature(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + LSM303DLHCMagState *s = LSM303DLHC_MAG(obj); + int64_t value; + + if (!visit_type_int(v, name, &value, errp)) { + return; + } + + /* Input temperature is in 0.001 C units. Convert to 1 lsb = 0.125 C. */ + value /= 125; + + if (value > 2047 || value < -2048) { + error_setg(errp, "value %" PRId64 " lsb is out of range", value); + return; + } + + s->temperature = (int16_t)value; +} + +/* + * Callback handler whenever a 'I2C_START_RECV' (read) event is received. + */ +static void lsm303dlhc_mag_read(LSM303DLHCMagState *s) +{ + /* + * Set the LOCK bit whenever a new read attempt is made. This will be + * cleared in I2C_FINISH. Note that DRDY is always set to 1 in this driver. + */ + s->sr = 0x3; + + /* + * Copy the current X/Y/Z and temp. values into the locked registers so + * that 'mag-x', 'mag-y', 'mag-z' and 'temperature' can continue to be + * updated via QOM, etc., without corrupting the current read event. + */ + s->x_lock = s->x; + s->z_lock = s->z; + s->y_lock = s->y; + s->temperature_lock = s->temperature; +} + +/* + * Callback handler whenever a 'I2C_FINISH' event is received. + */ +static void lsm303dlhc_mag_finish(LSM303DLHCMagState *s) +{ + /* + * Clear the LOCK bit when the read attempt terminates. + * This bit is initially set in the I2C_START_RECV handler. + */ + s->sr = 0x1; +} + +/* + * Callback handler when a device attempts to write to a register. + */ +static void lsm303dlhc_mag_write(LSM303DLHCMagState *s) +{ + switch (s->pointer) { + case LSM303DLHC_MAG_REG_CRA: + s->cra = s->buf; + break; + case LSM303DLHC_MAG_REG_CRB: + /* Make sure gain is at least 1, falling back to 1 on an error. */ + if (s->buf >> 5 == 0) { + s->buf = 1 << 5; + } + s->crb = s->buf; + break; + case LSM303DLHC_MAG_REG_MR: + s->mr = s->buf; + break; + case LSM303DLHC_MAG_REG_SR: + s->sr = s->buf; + break; + case LSM303DLHC_MAG_REG_IRA: + s->ira = s->buf; + break; + case LSM303DLHC_MAG_REG_IRB: + s->irb = s->buf; + break; + case LSM303DLHC_MAG_REG_IRC: + s->irc = s->buf; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "reg is read-only: 0x%02X", s->buf); + break; + } +} + +/* + * Low-level master-to-slave transaction handler. + */ +static int lsm303dlhc_mag_send(I2CSlave *i2c, uint8_t data) +{ + LSM303DLHCMagState *s = LSM303DLHC_MAG(i2c); + + if (s->len == 0) { + /* First byte is the reg pointer */ + s->pointer = data; + s->len++; + } else if (s->len == 1) { + /* Second byte is the new register value. */ + s->buf = data; + lsm303dlhc_mag_write(s); + } else { + g_assert_not_reached(); + } + + return 0; +} + +/* + * Low-level slave-to-master transaction handler (read attempts). + */ +static uint8_t lsm303dlhc_mag_recv(I2CSlave *i2c) +{ + LSM303DLHCMagState *s = LSM303DLHC_MAG(i2c); + uint8_t resp; + + switch (s->pointer) { + case LSM303DLHC_MAG_REG_CRA: + resp = s->cra; + break; + case LSM303DLHC_MAG_REG_CRB: + resp = s->crb; + break; + case LSM303DLHC_MAG_REG_MR: + resp = s->mr; + break; + case LSM303DLHC_MAG_REG_OUT_X_H: + resp = (uint8_t)(s->x_lock >> 8); + break; + case LSM303DLHC_MAG_REG_OUT_X_L: + resp = (uint8_t)(s->x_lock); + break; + case LSM303DLHC_MAG_REG_OUT_Z_H: + resp = (uint8_t)(s->z_lock >> 8); + break; + case LSM303DLHC_MAG_REG_OUT_Z_L: + resp = (uint8_t)(s->z_lock); + break; + case LSM303DLHC_MAG_REG_OUT_Y_H: + resp = (uint8_t)(s->y_lock >> 8); + break; + case LSM303DLHC_MAG_REG_OUT_Y_L: + resp = (uint8_t)(s->y_lock); + break; + case LSM303DLHC_MAG_REG_SR: + resp = s->sr; + break; + case LSM303DLHC_MAG_REG_IRA: + resp = s->ira; + break; + case LSM303DLHC_MAG_REG_IRB: + resp = s->irb; + break; + case LSM303DLHC_MAG_REG_IRC: + resp = s->irc; + break; + case LSM303DLHC_MAG_REG_TEMP_OUT_H: + /* Check if the temperature sensor is enabled or not (CRA & 0x80). */ + if (s->cra & 0x80) { + resp = (uint8_t)(s->temperature_lock >> 8); + } else { + resp = 0; + } + break; + case LSM303DLHC_MAG_REG_TEMP_OUT_L: + if (s->cra & 0x80) { + resp = (uint8_t)(s->temperature_lock & 0xff); + } else { + resp = 0; + } + break; + default: + resp = 0; + break; + } + + /* + * The address pointer on the LSM303DLHC auto-increments whenever a byte + * is read, without the master device having to request the next address. + * + * The auto-increment process has the following logic: + * + * - if (s->pointer == 8) then s->pointer = 3 + * - else: if (s->pointer == 12) then s->pointer = 0 + * - else: s->pointer += 1 + * + * Reading an invalid address return 0. + */ + if (s->pointer == LSM303DLHC_MAG_REG_OUT_Y_L) { + s->pointer = LSM303DLHC_MAG_REG_OUT_X_H; + } else if (s->pointer == LSM303DLHC_MAG_REG_IRC) { + s->pointer = LSM303DLHC_MAG_REG_CRA; + } else { + s->pointer++; + } + + return resp; +} + +/* + * Bus state change handler. + */ +static int lsm303dlhc_mag_event(I2CSlave *i2c, enum i2c_event event) +{ + LSM303DLHCMagState *s = LSM303DLHC_MAG(i2c); + + switch (event) { + case I2C_START_SEND: + break; + case I2C_START_RECV: + lsm303dlhc_mag_read(s); + break; + case I2C_FINISH: + lsm303dlhc_mag_finish(s); + break; + case I2C_NACK: + break; + } + + s->len = 0; + return 0; +} + +/* + * Device data description using VMSTATE macros. + */ +static const VMStateDescription vmstate_lsm303dlhc_mag = { + .name = "LSM303DLHC_MAG", + .version_id = 0, + .minimum_version_id = 0, + .fields = (VMStateField[]) { + + VMSTATE_I2C_SLAVE(parent_obj, LSM303DLHCMagState), + VMSTATE_UINT8(len, LSM303DLHCMagState), + VMSTATE_UINT8(buf, LSM303DLHCMagState), + VMSTATE_UINT8(pointer, LSM303DLHCMagState), + VMSTATE_UINT8(cra, LSM303DLHCMagState), + VMSTATE_UINT8(crb, LSM303DLHCMagState), + VMSTATE_UINT8(mr, LSM303DLHCMagState), + VMSTATE_INT16(x, LSM303DLHCMagState), + VMSTATE_INT16(z, LSM303DLHCMagState), + VMSTATE_INT16(y, LSM303DLHCMagState), + VMSTATE_INT16(x_lock, LSM303DLHCMagState), + VMSTATE_INT16(z_lock, LSM303DLHCMagState), + VMSTATE_INT16(y_lock, LSM303DLHCMagState), + VMSTATE_UINT8(sr, LSM303DLHCMagState), + VMSTATE_UINT8(ira, LSM303DLHCMagState), + VMSTATE_UINT8(irb, LSM303DLHCMagState), + VMSTATE_UINT8(irc, LSM303DLHCMagState), + VMSTATE_INT16(temperature, LSM303DLHCMagState), + VMSTATE_INT16(temperature_lock, LSM303DLHCMagState), + VMSTATE_END_OF_LIST() + } +}; + +/* + * Put the device into post-reset default state. + */ +static void lsm303dlhc_mag_default_cfg(LSM303DLHCMagState *s) +{ + /* Set the device into is default reset state. */ + s->len = 0; + s->pointer = 0; /* Current register. */ + s->buf = 0; /* Shared buffer. */ + s->cra = 0x10; /* Temp Enabled = 0, Data Rate = 15.0 Hz. */ + s->crb = 0x20; /* Gain = +/- 1.3 Gauss. */ + s->mr = 0x3; /* Operating Mode = Sleep. */ + s->x = 0; + s->z = 0; + s->y = 0; + s->x_lock = 0; + s->z_lock = 0; + s->y_lock = 0; + s->sr = 0x1; /* DRDY = 1. */ + s->ira = 0x48; + s->irb = 0x34; + s->irc = 0x33; + s->temperature = 0; /* Default to 0 degrees C (0/8 lsb = 0 C). */ + s->temperature_lock = 0; +} + +/* + * Callback handler when DeviceState 'reset' is set to true. + */ +static void lsm303dlhc_mag_reset(DeviceState *dev) +{ + I2CSlave *i2c = I2C_SLAVE(dev); + LSM303DLHCMagState *s = LSM303DLHC_MAG(i2c); + + /* Set the device into its default reset state. */ + lsm303dlhc_mag_default_cfg(s); +} + +/* + * Initialisation of any public properties. + */ +static void lsm303dlhc_mag_initfn(Object *obj) +{ + object_property_add(obj, "mag-x", "int", + lsm303dlhc_mag_get_x, + lsm303dlhc_mag_set_x, NULL, NULL); + + object_property_add(obj, "mag-y", "int", + lsm303dlhc_mag_get_y, + lsm303dlhc_mag_set_y, NULL, NULL); + + object_property_add(obj, "mag-z", "int", + lsm303dlhc_mag_get_z, + lsm303dlhc_mag_set_z, NULL, NULL); + + object_property_add(obj, "temperature", "int", + lsm303dlhc_mag_get_temperature, + lsm303dlhc_mag_set_temperature, NULL, NULL); +} + +/* + * Set the virtual method pointers (bus state change, tx/rx, etc.). + */ +static void lsm303dlhc_mag_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + I2CSlaveClass *k = I2C_SLAVE_CLASS(klass); + + dc->reset = lsm303dlhc_mag_reset; + dc->vmsd = &vmstate_lsm303dlhc_mag; + k->event = lsm303dlhc_mag_event; + k->recv = lsm303dlhc_mag_recv; + k->send = lsm303dlhc_mag_send; +} + +static const TypeInfo lsm303dlhc_mag_info = { + .name = TYPE_LSM303DLHC_MAG, + .parent = TYPE_I2C_SLAVE, + .instance_size = sizeof(LSM303DLHCMagState), + .instance_init = lsm303dlhc_mag_initfn, + .class_init = lsm303dlhc_mag_class_init, +}; + +static void lsm303dlhc_mag_register_types(void) +{ + type_register_static(&lsm303dlhc_mag_info); +} + +type_init(lsm303dlhc_mag_register_types) diff --git a/hw/sensor/meson.build b/hw/sensor/meson.build index 059c4ca935..d1bba290da 100644 --- a/hw/sensor/meson.build +++ b/hw/sensor/meson.build @@ -4,3 +4,4 @@ softmmu_ss.add(when: 'CONFIG_DPS310', if_true: files('dps310.c')) softmmu_ss.add(when: 'CONFIG_EMC141X', if_true: files('emc141x.c')) softmmu_ss.add(when: 'CONFIG_ADM1272', if_true: files('adm1272.c')) softmmu_ss.add(when: 'CONFIG_MAX34451', if_true: files('max34451.c')) +softmmu_ss.add(when: 'CONFIG_LSM303DLHC_MAG', if_true: files('lsm303dlhc_mag.c')) diff --git a/tests/qtest/lsm303dlhc-mag-test.c b/tests/qtest/lsm303dlhc-mag-test.c new file mode 100644 index 0000000000..0f64e7fc67 --- /dev/null +++ b/tests/qtest/lsm303dlhc-mag-test.c @@ -0,0 +1,148 @@ +/* + * QTest testcase for the LSM303DLHC I2C magnetometer + * + * Copyright (C) 2021 Linaro Ltd. + * Written by Kevin Townsend + * + * Based on: https://www.st.com/resource/en/datasheet/lsm303dlhc.pdf + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "libqtest-single.h" +#include "libqos/qgraph.h" +#include "libqos/i2c.h" +#include "qapi/qmp/qdict.h" + +#define LSM303DLHC_MAG_TEST_ID "lsm303dlhc_mag-test" +#define LSM303DLHC_MAG_REG_CRA 0x00 +#define LSM303DLHC_MAG_REG_CRB 0x01 +#define LSM303DLHC_MAG_REG_OUT_X_H 0x03 +#define LSM303DLHC_MAG_REG_OUT_Z_H 0x05 +#define LSM303DLHC_MAG_REG_OUT_Y_H 0x07 +#define LSM303DLHC_MAG_REG_IRC 0x0C +#define LSM303DLHC_MAG_REG_TEMP_OUT_H 0x31 + +static int qmp_lsm303dlhc_mag_get_property(const char *id, const char *prop) +{ + QDict *response; + int ret; + + response = qmp("{ 'execute': 'qom-get', 'arguments': { 'path': %s, " + "'property': %s } }", id, prop); + g_assert(qdict_haskey(response, "return")); + ret = qdict_get_int(response, "return"); + qobject_unref(response); + return ret; +} + +static void qmp_lsm303dlhc_mag_set_property(const char *id, const char *prop, + int value) +{ + QDict *response; + + response = qmp("{ 'execute': 'qom-set', 'arguments': { 'path': %s, " + "'property': %s, 'value': %d } }", id, prop, value); + g_assert(qdict_haskey(response, "return")); + qobject_unref(response); +} + +static void send_and_receive(void *obj, void *data, QGuestAllocator *alloc) +{ + int64_t value; + QI2CDevice *i2cdev = (QI2CDevice *)obj; + + /* Check default value for CRB */ + g_assert_cmphex(i2c_get8(i2cdev, LSM303DLHC_MAG_REG_CRB), ==, 0x20); + + /* Set x to 1.0 gauss and verify the value */ + qmp_lsm303dlhc_mag_set_property(LSM303DLHC_MAG_TEST_ID, "mag-x", 100000); + value = qmp_lsm303dlhc_mag_get_property( + LSM303DLHC_MAG_TEST_ID, "mag-x"); + g_assert_cmpint(value, ==, 100000); + + /* Set y to 1.5 gauss and verify the value */ + qmp_lsm303dlhc_mag_set_property(LSM303DLHC_MAG_TEST_ID, "mag-y", 150000); + value = qmp_lsm303dlhc_mag_get_property( + LSM303DLHC_MAG_TEST_ID, "mag-y"); + g_assert_cmpint(value, ==, 150000); + + /* Set z to 0.5 gauss and verify the value */ + qmp_lsm303dlhc_mag_set_property(LSM303DLHC_MAG_TEST_ID, "mag-z", 50000); + value = qmp_lsm303dlhc_mag_get_property( + LSM303DLHC_MAG_TEST_ID, "mag-z"); + g_assert_cmpint(value, ==, 50000); + + /* Set temperature to 23.6 C and verify the value */ + qmp_lsm303dlhc_mag_set_property(LSM303DLHC_MAG_TEST_ID, + "temperature", 23600); + value = qmp_lsm303dlhc_mag_get_property( + LSM303DLHC_MAG_TEST_ID, "temperature"); + /* Should return 23.5 C due to 0.125°C steps. */ + g_assert_cmpint(value, ==, 23500); + + /* Read raw x axis registers (1 gauss = 1100 at +/-1.3 g gain) */ + value = i2c_get16(i2cdev, LSM303DLHC_MAG_REG_OUT_X_H); + g_assert_cmphex(value, ==, 1100); + + /* Read raw y axis registers (1.5 gauss = 1650 at +/- 1.3 g gain = ) */ + value = i2c_get16(i2cdev, LSM303DLHC_MAG_REG_OUT_Y_H); + g_assert_cmphex(value, ==, 1650); + + /* Read raw z axis registers (0.5 gauss = 490 at +/- 1.3 g gain = ) */ + value = i2c_get16(i2cdev, LSM303DLHC_MAG_REG_OUT_Z_H); + g_assert_cmphex(value, ==, 490); + + /* Read raw temperature registers with temp disabled (CRA = 0x10) */ + value = i2c_get16(i2cdev, LSM303DLHC_MAG_REG_TEMP_OUT_H); + g_assert_cmphex(value, ==, 0); + + /* Enable temperature reads (CRA = 0x90) */ + i2c_set8(i2cdev, LSM303DLHC_MAG_REG_CRA, 0x90); + + /* Read raw temp registers (23.5 C = 188 at 1 lsb = 0.125 C) */ + value = i2c_get16(i2cdev, LSM303DLHC_MAG_REG_TEMP_OUT_H); + g_assert_cmphex(value, ==, 188); +} + +static void reg_wraparound(void *obj, void *data, QGuestAllocator *alloc) +{ + uint8_t value[4]; + QI2CDevice *i2cdev = (QI2CDevice *)obj; + + /* Set x to 1.0 gauss, and y to 1.5 gauss for known test values */ + qmp_lsm303dlhc_mag_set_property(LSM303DLHC_MAG_TEST_ID, "mag-x", 100000); + qmp_lsm303dlhc_mag_set_property(LSM303DLHC_MAG_TEST_ID, "mag-y", 150000); + + /* Check that requesting 4 bytes starting at Y_H wraps around to X_L */ + i2c_read_block(i2cdev, LSM303DLHC_MAG_REG_OUT_Y_H, value, 4); + /* 1.5 gauss = 1650 lsb = 0x672 */ + g_assert_cmphex(value[0], ==, 0x06); + g_assert_cmphex(value[1], ==, 0x72); + /* 1.0 gauss = 1100 lsb = 0x44C */ + g_assert_cmphex(value[2], ==, 0x04); + g_assert_cmphex(value[3], ==, 0x4C); + + /* Check that requesting LSM303DLHC_MAG_REG_IRC wraps around to CRA */ + i2c_read_block(i2cdev, LSM303DLHC_MAG_REG_IRC, value, 2); + /* Default value for IRC = 0x33 */ + g_assert_cmphex(value[0], ==, 0x33); + /* Default value for CRA = 0x10 */ + g_assert_cmphex(value[1], ==, 0x10); +} + +static void lsm303dlhc_mag_register_nodes(void) +{ + QOSGraphEdgeOptions opts = { + .extra_device_opts = "id=" LSM303DLHC_MAG_TEST_ID ",address=0x1e" + }; + add_qi2c_address(&opts, &(QI2CAddress) { 0x1E }); + + qos_node_create_driver("lsm303dlhc_mag", i2c_device_create); + qos_node_consumes("lsm303dlhc_mag", "i2c-bus", &opts); + + qos_add_test("tx-rx", "lsm303dlhc_mag", send_and_receive, NULL); + qos_add_test("regwrap", "lsm303dlhc_mag", reg_wraparound, NULL); +} +libqos_init(lsm303dlhc_mag_register_nodes); diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index 762f6adcd5..f33d84d19b 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -243,6 +243,7 @@ qos_test_ss.add( 'eepro100-test.c', 'es1370-test.c', 'ipoctal232-test.c', + 'lsm303dlhc-mag-test.c', 'max34451-test.c', 'megasas-test.c', 'ne2000-test.c', From 620d0b49a40e24465472b667f19b5fb0c63a6f0c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 8 Feb 2022 05:54:29 +0300 Subject: [PATCH 295/460] common-user/host/sparc64: Fix safe_syscall_base Use the "retl" instead of "ret" instruction alias, since we do not allocate a register window in this function. Fix the offset to the first stacked parameter, which lies beyond the register window save area. Fixes: 95c021dac835 ("linux-user/host/sparc64: Add safe-syscall.inc.S") Signed-off-by: Richard Henderson --- common-user/host/sparc64/safe-syscall.inc.S | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/common-user/host/sparc64/safe-syscall.inc.S b/common-user/host/sparc64/safe-syscall.inc.S index a2f2b9c967..c7be8f2d25 100644 --- a/common-user/host/sparc64/safe-syscall.inc.S +++ b/common-user/host/sparc64/safe-syscall.inc.S @@ -24,7 +24,8 @@ .type safe_syscall_end, @function #define STACK_BIAS 2047 -#define PARAM(N) STACK_BIAS + N*8 +#define WINDOW_SIZE 16 * 8 +#define PARAM(N) STACK_BIAS + WINDOW_SIZE + N * 8 /* * This is the entry point for making a system call. The calling @@ -74,7 +75,7 @@ safe_syscall_end: /* code path for having successfully executed the syscall */ bcs,pn %xcc, 1f nop - ret + retl nop /* code path when we didn't execute the syscall */ From c8c89a6a30be0e6f24e6a56d4ef181ec0e4dd064 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 8 Feb 2022 09:30:42 +0300 Subject: [PATCH 296/460] linux-user: Introduce host_signal_mask MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Do not directly access the uc_sigmask member. This is preparation for a sparc64 fix. Reviewed-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- linux-user/include/host/aarch64/host-signal.h | 5 +++++ linux-user/include/host/alpha/host-signal.h | 5 +++++ linux-user/include/host/arm/host-signal.h | 5 +++++ linux-user/include/host/i386/host-signal.h | 5 +++++ .../include/host/loongarch64/host-signal.h | 5 +++++ linux-user/include/host/mips/host-signal.h | 5 +++++ linux-user/include/host/ppc/host-signal.h | 5 +++++ linux-user/include/host/riscv/host-signal.h | 5 +++++ linux-user/include/host/s390/host-signal.h | 5 +++++ linux-user/include/host/sparc/host-signal.h | 5 +++++ linux-user/include/host/x86_64/host-signal.h | 5 +++++ linux-user/signal.c | 18 ++++++++---------- 12 files changed, 63 insertions(+), 10 deletions(-) diff --git a/linux-user/include/host/aarch64/host-signal.h b/linux-user/include/host/aarch64/host-signal.h index 9770b36dc1..76ab078069 100644 --- a/linux-user/include/host/aarch64/host-signal.h +++ b/linux-user/include/host/aarch64/host-signal.h @@ -40,6 +40,11 @@ static inline void host_signal_set_pc(ucontext_t *uc, uintptr_t pc) uc->uc_mcontext.pc = pc; } +static inline void *host_signal_mask(ucontext_t *uc) +{ + return &uc->uc_sigmask; +} + static inline bool host_signal_write(siginfo_t *info, ucontext_t *uc) { struct _aarch64_ctx *hdr; diff --git a/linux-user/include/host/alpha/host-signal.h b/linux-user/include/host/alpha/host-signal.h index f4c942948a..a44d670f2b 100644 --- a/linux-user/include/host/alpha/host-signal.h +++ b/linux-user/include/host/alpha/host-signal.h @@ -21,6 +21,11 @@ static inline void host_signal_set_pc(ucontext_t *uc, uintptr_t pc) uc->uc_mcontext.sc_pc = pc; } +static inline void *host_signal_mask(ucontext_t *uc) +{ + return &uc->uc_sigmask; +} + static inline bool host_signal_write(siginfo_t *info, ucontext_t *uc) { uint32_t *pc = (uint32_t *)host_signal_pc(uc); diff --git a/linux-user/include/host/arm/host-signal.h b/linux-user/include/host/arm/host-signal.h index 6c095773c0..bbeb4ffefb 100644 --- a/linux-user/include/host/arm/host-signal.h +++ b/linux-user/include/host/arm/host-signal.h @@ -21,6 +21,11 @@ static inline void host_signal_set_pc(ucontext_t *uc, uintptr_t pc) uc->uc_mcontext.arm_pc = pc; } +static inline void *host_signal_mask(ucontext_t *uc) +{ + return &uc->uc_sigmask; +} + static inline bool host_signal_write(siginfo_t *info, ucontext_t *uc) { /* diff --git a/linux-user/include/host/i386/host-signal.h b/linux-user/include/host/i386/host-signal.h index abe1ece5c9..fd36f06bda 100644 --- a/linux-user/include/host/i386/host-signal.h +++ b/linux-user/include/host/i386/host-signal.h @@ -21,6 +21,11 @@ static inline void host_signal_set_pc(ucontext_t *uc, uintptr_t pc) uc->uc_mcontext.gregs[REG_EIP] = pc; } +static inline void *host_signal_mask(ucontext_t *uc) +{ + return &uc->uc_sigmask; +} + static inline bool host_signal_write(siginfo_t *info, ucontext_t *uc) { return uc->uc_mcontext.gregs[REG_TRAPNO] == 0xe diff --git a/linux-user/include/host/loongarch64/host-signal.h b/linux-user/include/host/loongarch64/host-signal.h index 7effa24251..a9dfe0c688 100644 --- a/linux-user/include/host/loongarch64/host-signal.h +++ b/linux-user/include/host/loongarch64/host-signal.h @@ -21,6 +21,11 @@ static inline void host_signal_set_pc(ucontext_t *uc, uintptr_t pc) uc->uc_mcontext.__pc = pc; } +static inline void *host_signal_mask(ucontext_t *uc) +{ + return &uc->uc_sigmask; +} + static inline bool host_signal_write(siginfo_t *info, ucontext_t *uc) { const uint32_t *pinsn = (const uint32_t *)host_signal_pc(uc); diff --git a/linux-user/include/host/mips/host-signal.h b/linux-user/include/host/mips/host-signal.h index c666ed8c3f..ff840dd491 100644 --- a/linux-user/include/host/mips/host-signal.h +++ b/linux-user/include/host/mips/host-signal.h @@ -21,6 +21,11 @@ static inline void host_signal_set_pc(ucontext_t *uc, uintptr_t pc) uc->uc_mcontext.pc = pc; } +static inline void *host_signal_mask(ucontext_t *uc) +{ + return &uc->uc_sigmask; +} + #if defined(__misp16) || defined(__mips_micromips) #error "Unsupported encoding" #endif diff --git a/linux-user/include/host/ppc/host-signal.h b/linux-user/include/host/ppc/host-signal.h index 1d8e658ff7..730a321d98 100644 --- a/linux-user/include/host/ppc/host-signal.h +++ b/linux-user/include/host/ppc/host-signal.h @@ -21,6 +21,11 @@ static inline void host_signal_set_pc(ucontext_t *uc, uintptr_t pc) uc->uc_mcontext.regs->nip = pc; } +static inline void *host_signal_mask(ucontext_t *uc) +{ + return &uc->uc_sigmask; +} + static inline bool host_signal_write(siginfo_t *info, ucontext_t *uc) { return uc->uc_mcontext.regs->trap != 0x400 diff --git a/linux-user/include/host/riscv/host-signal.h b/linux-user/include/host/riscv/host-signal.h index a4f170efb0..aceae544f2 100644 --- a/linux-user/include/host/riscv/host-signal.h +++ b/linux-user/include/host/riscv/host-signal.h @@ -21,6 +21,11 @@ static inline void host_signal_set_pc(ucontext_t *uc, uintptr_t pc) uc->uc_mcontext.__gregs[REG_PC] = pc; } +static inline void *host_signal_mask(ucontext_t *uc) +{ + return &uc->uc_sigmask; +} + static inline bool host_signal_write(siginfo_t *info, ucontext_t *uc) { /* diff --git a/linux-user/include/host/s390/host-signal.h b/linux-user/include/host/s390/host-signal.h index a524f2ab00..e454cea54a 100644 --- a/linux-user/include/host/s390/host-signal.h +++ b/linux-user/include/host/s390/host-signal.h @@ -21,6 +21,11 @@ static inline void host_signal_set_pc(ucontext_t *uc, uintptr_t pc) uc->uc_mcontext.psw.addr = pc; } +static inline void *host_signal_mask(ucontext_t *uc) +{ + return &uc->uc_sigmask; +} + static inline bool host_signal_write(siginfo_t *info, ucontext_t *uc) { uint16_t *pinsn = (uint16_t *)host_signal_pc(uc); diff --git a/linux-user/include/host/sparc/host-signal.h b/linux-user/include/host/sparc/host-signal.h index 7342936071..158918f2ec 100644 --- a/linux-user/include/host/sparc/host-signal.h +++ b/linux-user/include/host/sparc/host-signal.h @@ -29,6 +29,11 @@ static inline void host_signal_set_pc(ucontext_t *uc, uintptr_t pc) #endif } +static inline void *host_signal_mask(ucontext_t *uc) +{ + return &uc->uc_sigmask; +} + static inline bool host_signal_write(siginfo_t *info, ucontext_t *uc) { uint32_t insn = *(uint32_t *)host_signal_pc(uc); diff --git a/linux-user/include/host/x86_64/host-signal.h b/linux-user/include/host/x86_64/host-signal.h index c71d597eb2..d64d076625 100644 --- a/linux-user/include/host/x86_64/host-signal.h +++ b/linux-user/include/host/x86_64/host-signal.h @@ -20,6 +20,11 @@ static inline void host_signal_set_pc(ucontext_t *uc, uintptr_t pc) uc->uc_mcontext.gregs[REG_RIP] = pc; } +static inline void *host_signal_mask(ucontext_t *uc) +{ + return &uc->uc_sigmask; +} + static inline bool host_signal_write(siginfo_t *info, ucontext_t *uc) { return uc->uc_mcontext.gregs[REG_TRAPNO] == 0xe diff --git a/linux-user/signal.c b/linux-user/signal.c index 32854bb375..0c61459d4a 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -820,6 +820,7 @@ static void host_signal_handler(int host_sig, siginfo_t *info, void *puc) int guest_sig; uintptr_t pc = 0; bool sync_sig = false; + void *sigmask = host_signal_mask(uc); /* * Non-spoofed SIGSEGV and SIGBUS are synchronous, and need special @@ -849,8 +850,7 @@ static void host_signal_handler(int host_sig, siginfo_t *info, void *puc) if (info->si_code == SEGV_ACCERR && h2g_valid(host_addr)) { /* If this was a write to a TB protected page, restart. */ if (is_write && - handle_sigsegv_accerr_write(cpu, &uc->uc_sigmask, - pc, guest_addr)) { + handle_sigsegv_accerr_write(cpu, sigmask, pc, guest_addr)) { return; } @@ -865,10 +865,10 @@ static void host_signal_handler(int host_sig, siginfo_t *info, void *puc) } } - sigprocmask(SIG_SETMASK, &uc->uc_sigmask, NULL); + sigprocmask(SIG_SETMASK, sigmask, NULL); cpu_loop_exit_sigsegv(cpu, guest_addr, access_type, maperr, pc); } else { - sigprocmask(SIG_SETMASK, &uc->uc_sigmask, NULL); + sigprocmask(SIG_SETMASK, sigmask, NULL); if (info->si_code == BUS_ADRALN) { cpu_loop_exit_sigbus(cpu, guest_addr, access_type, pc); } @@ -909,17 +909,15 @@ static void host_signal_handler(int host_sig, siginfo_t *info, void *puc) * now and it getting out to the main loop. Signals will be * unblocked again in process_pending_signals(). * - * WARNING: we cannot use sigfillset() here because the uc_sigmask + * WARNING: we cannot use sigfillset() here because the sigmask * field is a kernel sigset_t, which is much smaller than the * libc sigset_t which sigfillset() operates on. Using sigfillset() * would write 0xff bytes off the end of the structure and trash * data on the struct. - * We can't use sizeof(uc->uc_sigmask) either, because the libc - * headers define the struct field with the wrong (too large) type. */ - memset(&uc->uc_sigmask, 0xff, SIGSET_T_SIZE); - sigdelset(&uc->uc_sigmask, SIGSEGV); - sigdelset(&uc->uc_sigmask, SIGBUS); + memset(sigmask, 0xff, SIGSET_T_SIZE); + sigdelset(sigmask, SIGSEGV); + sigdelset(sigmask, SIGBUS); /* interrupt the virtual CPU as soon as possible */ cpu_exit(thread_cpu); From 9940799bdea5d456fdbc11d10f355755843063e9 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 8 Feb 2022 09:40:00 +0300 Subject: [PATCH 297/460] linux-user: Introduce host_sigcontext MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Do not directly access ucontext_t as the third signal parameter. This is preparation for a sparc64 fix. Reviewed-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- linux-user/include/host/aarch64/host-signal.h | 13 ++++++++----- linux-user/include/host/alpha/host-signal.h | 11 +++++++---- linux-user/include/host/arm/host-signal.h | 11 +++++++---- linux-user/include/host/i386/host-signal.h | 11 +++++++---- linux-user/include/host/loongarch64/host-signal.h | 11 +++++++---- linux-user/include/host/mips/host-signal.h | 11 +++++++---- linux-user/include/host/ppc/host-signal.h | 11 +++++++---- linux-user/include/host/riscv/host-signal.h | 11 +++++++---- linux-user/include/host/s390/host-signal.h | 11 +++++++---- linux-user/include/host/sparc/host-signal.h | 11 +++++++---- linux-user/include/host/x86_64/host-signal.h | 11 +++++++---- linux-user/signal.c | 4 ++-- 12 files changed, 80 insertions(+), 47 deletions(-) diff --git a/linux-user/include/host/aarch64/host-signal.h b/linux-user/include/host/aarch64/host-signal.h index 76ab078069..be079684a2 100644 --- a/linux-user/include/host/aarch64/host-signal.h +++ b/linux-user/include/host/aarch64/host-signal.h @@ -11,6 +11,9 @@ #ifndef AARCH64_HOST_SIGNAL_H #define AARCH64_HOST_SIGNAL_H +/* The third argument to a SA_SIGINFO handler is ucontext_t. */ +typedef ucontext_t host_sigcontext; + /* Pre-3.16 kernel headers don't have these, so provide fallback definitions */ #ifndef ESR_MAGIC #define ESR_MAGIC 0x45535201 @@ -20,7 +23,7 @@ struct esr_context { }; #endif -static inline struct _aarch64_ctx *first_ctx(ucontext_t *uc) +static inline struct _aarch64_ctx *first_ctx(host_sigcontext *uc) { return (struct _aarch64_ctx *)&uc->uc_mcontext.__reserved; } @@ -30,22 +33,22 @@ static inline struct _aarch64_ctx *next_ctx(struct _aarch64_ctx *hdr) return (struct _aarch64_ctx *)((char *)hdr + hdr->size); } -static inline uintptr_t host_signal_pc(ucontext_t *uc) +static inline uintptr_t host_signal_pc(host_sigcontext *uc) { return uc->uc_mcontext.pc; } -static inline void host_signal_set_pc(ucontext_t *uc, uintptr_t pc) +static inline void host_signal_set_pc(host_sigcontext *uc, uintptr_t pc) { uc->uc_mcontext.pc = pc; } -static inline void *host_signal_mask(ucontext_t *uc) +static inline void *host_signal_mask(host_sigcontext *uc) { return &uc->uc_sigmask; } -static inline bool host_signal_write(siginfo_t *info, ucontext_t *uc) +static inline bool host_signal_write(siginfo_t *info, host_sigcontext *uc) { struct _aarch64_ctx *hdr; uint32_t insn; diff --git a/linux-user/include/host/alpha/host-signal.h b/linux-user/include/host/alpha/host-signal.h index a44d670f2b..4f9e2abc4b 100644 --- a/linux-user/include/host/alpha/host-signal.h +++ b/linux-user/include/host/alpha/host-signal.h @@ -11,22 +11,25 @@ #ifndef ALPHA_HOST_SIGNAL_H #define ALPHA_HOST_SIGNAL_H -static inline uintptr_t host_signal_pc(ucontext_t *uc) +/* The third argument to a SA_SIGINFO handler is ucontext_t. */ +typedef ucontext_t host_sigcontext; + +static inline uintptr_t host_signal_pc(host_sigcontext *uc) { return uc->uc_mcontext.sc_pc; } -static inline void host_signal_set_pc(ucontext_t *uc, uintptr_t pc) +static inline void host_signal_set_pc(host_sigcontext *uc, uintptr_t pc) { uc->uc_mcontext.sc_pc = pc; } -static inline void *host_signal_mask(ucontext_t *uc) +static inline void *host_signal_mask(host_sigcontext *uc) { return &uc->uc_sigmask; } -static inline bool host_signal_write(siginfo_t *info, ucontext_t *uc) +static inline bool host_signal_write(siginfo_t *info, host_sigcontext *uc) { uint32_t *pc = (uint32_t *)host_signal_pc(uc); uint32_t insn = *pc; diff --git a/linux-user/include/host/arm/host-signal.h b/linux-user/include/host/arm/host-signal.h index bbeb4ffefb..faba496d24 100644 --- a/linux-user/include/host/arm/host-signal.h +++ b/linux-user/include/host/arm/host-signal.h @@ -11,22 +11,25 @@ #ifndef ARM_HOST_SIGNAL_H #define ARM_HOST_SIGNAL_H -static inline uintptr_t host_signal_pc(ucontext_t *uc) +/* The third argument to a SA_SIGINFO handler is ucontext_t. */ +typedef ucontext_t host_sigcontext; + +static inline uintptr_t host_signal_pc(host_sigcontext *uc) { return uc->uc_mcontext.arm_pc; } -static inline void host_signal_set_pc(ucontext_t *uc, uintptr_t pc) +static inline void host_signal_set_pc(host_sigcontext *uc, uintptr_t pc) { uc->uc_mcontext.arm_pc = pc; } -static inline void *host_signal_mask(ucontext_t *uc) +static inline void *host_signal_mask(host_sigcontext *uc) { return &uc->uc_sigmask; } -static inline bool host_signal_write(siginfo_t *info, ucontext_t *uc) +static inline bool host_signal_write(siginfo_t *info, host_sigcontext *uc) { /* * In the FSR, bit 11 is WnR, assuming a v6 or diff --git a/linux-user/include/host/i386/host-signal.h b/linux-user/include/host/i386/host-signal.h index fd36f06bda..e2b64f077f 100644 --- a/linux-user/include/host/i386/host-signal.h +++ b/linux-user/include/host/i386/host-signal.h @@ -11,22 +11,25 @@ #ifndef I386_HOST_SIGNAL_H #define I386_HOST_SIGNAL_H -static inline uintptr_t host_signal_pc(ucontext_t *uc) +/* The third argument to a SA_SIGINFO handler is ucontext_t. */ +typedef ucontext_t host_sigcontext; + +static inline uintptr_t host_signal_pc(host_sigcontext *uc) { return uc->uc_mcontext.gregs[REG_EIP]; } -static inline void host_signal_set_pc(ucontext_t *uc, uintptr_t pc) +static inline void host_signal_set_pc(host_sigcontext *uc, uintptr_t pc) { uc->uc_mcontext.gregs[REG_EIP] = pc; } -static inline void *host_signal_mask(ucontext_t *uc) +static inline void *host_signal_mask(host_sigcontext *uc) { return &uc->uc_sigmask; } -static inline bool host_signal_write(siginfo_t *info, ucontext_t *uc) +static inline bool host_signal_write(siginfo_t *info, host_sigcontext *uc) { return uc->uc_mcontext.gregs[REG_TRAPNO] == 0xe && (uc->uc_mcontext.gregs[REG_ERR] & 0x2); diff --git a/linux-user/include/host/loongarch64/host-signal.h b/linux-user/include/host/loongarch64/host-signal.h index a9dfe0c688..d33c3fc03e 100644 --- a/linux-user/include/host/loongarch64/host-signal.h +++ b/linux-user/include/host/loongarch64/host-signal.h @@ -11,22 +11,25 @@ #ifndef LOONGARCH64_HOST_SIGNAL_H #define LOONGARCH64_HOST_SIGNAL_H -static inline uintptr_t host_signal_pc(ucontext_t *uc) +/* The third argument to a SA_SIGINFO handler is ucontext_t. */ +typedef ucontext_t host_sigcontext; + +static inline uintptr_t host_signal_pc(host_sigcontext *uc) { return uc->uc_mcontext.__pc; } -static inline void host_signal_set_pc(ucontext_t *uc, uintptr_t pc) +static inline void host_signal_set_pc(host_sigcontext *uc, uintptr_t pc) { uc->uc_mcontext.__pc = pc; } -static inline void *host_signal_mask(ucontext_t *uc) +static inline void *host_signal_mask(host_sigcontext *uc) { return &uc->uc_sigmask; } -static inline bool host_signal_write(siginfo_t *info, ucontext_t *uc) +static inline bool host_signal_write(siginfo_t *info, host_sigcontext *uc) { const uint32_t *pinsn = (const uint32_t *)host_signal_pc(uc); uint32_t insn = pinsn[0]; diff --git a/linux-user/include/host/mips/host-signal.h b/linux-user/include/host/mips/host-signal.h index ff840dd491..0dbc5cecfd 100644 --- a/linux-user/include/host/mips/host-signal.h +++ b/linux-user/include/host/mips/host-signal.h @@ -11,17 +11,20 @@ #ifndef MIPS_HOST_SIGNAL_H #define MIPS_HOST_SIGNAL_H -static inline uintptr_t host_signal_pc(ucontext_t *uc) +/* The third argument to a SA_SIGINFO handler is ucontext_t. */ +typedef ucontext_t host_sigcontext; + +static inline uintptr_t host_signal_pc(host_sigcontext *uc) { return uc->uc_mcontext.pc; } -static inline void host_signal_set_pc(ucontext_t *uc, uintptr_t pc) +static inline void host_signal_set_pc(host_sigcontext *uc, uintptr_t pc) { uc->uc_mcontext.pc = pc; } -static inline void *host_signal_mask(ucontext_t *uc) +static inline void *host_signal_mask(host_sigcontext *uc) { return &uc->uc_sigmask; } @@ -30,7 +33,7 @@ static inline void *host_signal_mask(ucontext_t *uc) #error "Unsupported encoding" #endif -static inline bool host_signal_write(siginfo_t *info, ucontext_t *uc) +static inline bool host_signal_write(siginfo_t *info, host_sigcontext *uc) { uint32_t insn = *(uint32_t *)host_signal_pc(uc); diff --git a/linux-user/include/host/ppc/host-signal.h b/linux-user/include/host/ppc/host-signal.h index 730a321d98..b80384d135 100644 --- a/linux-user/include/host/ppc/host-signal.h +++ b/linux-user/include/host/ppc/host-signal.h @@ -11,22 +11,25 @@ #ifndef PPC_HOST_SIGNAL_H #define PPC_HOST_SIGNAL_H -static inline uintptr_t host_signal_pc(ucontext_t *uc) +/* The third argument to a SA_SIGINFO handler is ucontext_t. */ +typedef ucontext_t host_sigcontext; + +static inline uintptr_t host_signal_pc(host_sigcontext *uc) { return uc->uc_mcontext.regs->nip; } -static inline void host_signal_set_pc(ucontext_t *uc, uintptr_t pc) +static inline void host_signal_set_pc(host_sigcontext *uc, uintptr_t pc) { uc->uc_mcontext.regs->nip = pc; } -static inline void *host_signal_mask(ucontext_t *uc) +static inline void *host_signal_mask(host_sigcontext *uc) { return &uc->uc_sigmask; } -static inline bool host_signal_write(siginfo_t *info, ucontext_t *uc) +static inline bool host_signal_write(siginfo_t *info, host_sigcontext *uc) { return uc->uc_mcontext.regs->trap != 0x400 && (uc->uc_mcontext.regs->dsisr & 0x02000000); diff --git a/linux-user/include/host/riscv/host-signal.h b/linux-user/include/host/riscv/host-signal.h index aceae544f2..decacb2325 100644 --- a/linux-user/include/host/riscv/host-signal.h +++ b/linux-user/include/host/riscv/host-signal.h @@ -11,22 +11,25 @@ #ifndef RISCV_HOST_SIGNAL_H #define RISCV_HOST_SIGNAL_H -static inline uintptr_t host_signal_pc(ucontext_t *uc) +/* The third argument to a SA_SIGINFO handler is ucontext_t. */ +typedef ucontext_t host_sigcontext; + +static inline uintptr_t host_signal_pc(host_sigcontext *uc) { return uc->uc_mcontext.__gregs[REG_PC]; } -static inline void host_signal_set_pc(ucontext_t *uc, uintptr_t pc) +static inline void host_signal_set_pc(host_sigcontext *uc, uintptr_t pc) { uc->uc_mcontext.__gregs[REG_PC] = pc; } -static inline void *host_signal_mask(ucontext_t *uc) +static inline void *host_signal_mask(host_sigcontext *uc) { return &uc->uc_sigmask; } -static inline bool host_signal_write(siginfo_t *info, ucontext_t *uc) +static inline bool host_signal_write(siginfo_t *info, host_sigcontext *uc) { /* * Detect store by reading the instruction at the program counter. diff --git a/linux-user/include/host/s390/host-signal.h b/linux-user/include/host/s390/host-signal.h index e454cea54a..6f191e64d7 100644 --- a/linux-user/include/host/s390/host-signal.h +++ b/linux-user/include/host/s390/host-signal.h @@ -11,22 +11,25 @@ #ifndef S390_HOST_SIGNAL_H #define S390_HOST_SIGNAL_H -static inline uintptr_t host_signal_pc(ucontext_t *uc) +/* The third argument to a SA_SIGINFO handler is ucontext_t. */ +typedef ucontext_t host_sigcontext; + +static inline uintptr_t host_signal_pc(host_sigcontext *uc) { return uc->uc_mcontext.psw.addr; } -static inline void host_signal_set_pc(ucontext_t *uc, uintptr_t pc) +static inline void host_signal_set_pc(host_sigcontext *uc, uintptr_t pc) { uc->uc_mcontext.psw.addr = pc; } -static inline void *host_signal_mask(ucontext_t *uc) +static inline void *host_signal_mask(host_sigcontext *uc) { return &uc->uc_sigmask; } -static inline bool host_signal_write(siginfo_t *info, ucontext_t *uc) +static inline bool host_signal_write(siginfo_t *info, host_sigcontext *uc) { uint16_t *pinsn = (uint16_t *)host_signal_pc(uc); diff --git a/linux-user/include/host/sparc/host-signal.h b/linux-user/include/host/sparc/host-signal.h index 158918f2ec..871b6bb269 100644 --- a/linux-user/include/host/sparc/host-signal.h +++ b/linux-user/include/host/sparc/host-signal.h @@ -11,7 +11,10 @@ #ifndef SPARC_HOST_SIGNAL_H #define SPARC_HOST_SIGNAL_H -static inline uintptr_t host_signal_pc(ucontext_t *uc) +/* FIXME: the third argument to a SA_SIGINFO handler is *not* ucontext_t. */ +typedef ucontext_t host_sigcontext; + +static inline uintptr_t host_signal_pc(host_sigcontext *uc) { #ifdef __arch64__ return uc->uc_mcontext.mc_gregs[MC_PC]; @@ -20,7 +23,7 @@ static inline uintptr_t host_signal_pc(ucontext_t *uc) #endif } -static inline void host_signal_set_pc(ucontext_t *uc, uintptr_t pc) +static inline void host_signal_set_pc(host_sigcontext *uc, uintptr_t pc) { #ifdef __arch64__ uc->uc_mcontext.mc_gregs[MC_PC] = pc; @@ -29,12 +32,12 @@ static inline void host_signal_set_pc(ucontext_t *uc, uintptr_t pc) #endif } -static inline void *host_signal_mask(ucontext_t *uc) +static inline void *host_signal_mask(host_sigcontext *uc) { return &uc->uc_sigmask; } -static inline bool host_signal_write(siginfo_t *info, ucontext_t *uc) +static inline bool host_signal_write(siginfo_t *info, host_sigcontext *uc) { uint32_t insn = *(uint32_t *)host_signal_pc(uc); diff --git a/linux-user/include/host/x86_64/host-signal.h b/linux-user/include/host/x86_64/host-signal.h index d64d076625..5a7627fedc 100644 --- a/linux-user/include/host/x86_64/host-signal.h +++ b/linux-user/include/host/x86_64/host-signal.h @@ -10,22 +10,25 @@ #ifndef X86_64_HOST_SIGNAL_H #define X86_64_HOST_SIGNAL_H -static inline uintptr_t host_signal_pc(ucontext_t *uc) +/* The third argument to a SA_SIGINFO handler is ucontext_t. */ +typedef ucontext_t host_sigcontext; + +static inline uintptr_t host_signal_pc(host_sigcontext *uc) { return uc->uc_mcontext.gregs[REG_RIP]; } -static inline void host_signal_set_pc(ucontext_t *uc, uintptr_t pc) +static inline void host_signal_set_pc(host_sigcontext *uc, uintptr_t pc) { uc->uc_mcontext.gregs[REG_RIP] = pc; } -static inline void *host_signal_mask(ucontext_t *uc) +static inline void *host_signal_mask(host_sigcontext *uc) { return &uc->uc_sigmask; } -static inline bool host_signal_write(siginfo_t *info, ucontext_t *uc) +static inline bool host_signal_write(siginfo_t *info, host_sigcontext *uc) { return uc->uc_mcontext.gregs[REG_TRAPNO] == 0xe && (uc->uc_mcontext.gregs[REG_ERR] & 0x2); diff --git a/linux-user/signal.c b/linux-user/signal.c index 0c61459d4a..27a0ff30e9 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -800,7 +800,7 @@ void queue_signal(CPUArchState *env, int sig, int si_type, /* Adjust the signal context to rewind out of safe-syscall if we're in it */ static inline void rewind_if_in_safe_syscall(void *puc) { - ucontext_t *uc = (ucontext_t *)puc; + host_sigcontext *uc = (host_sigcontext *)puc; uintptr_t pcreg = host_signal_pc(uc); if (pcreg > (uintptr_t)safe_syscall_start @@ -815,7 +815,7 @@ static void host_signal_handler(int host_sig, siginfo_t *info, void *puc) CPUState *cpu = env_cpu(env); TaskState *ts = cpu->opaque; target_siginfo_t tinfo; - ucontext_t *uc = puc; + host_sigcontext *uc = puc; struct emulated_sigtable *k; int guest_sig; uintptr_t pc = 0; From 238b32de39b77b01ef4f205e09b01fda8a3a9216 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 8 Feb 2022 09:42:13 +0300 Subject: [PATCH 298/460] linux-user: Move sparc/host-signal.h to sparc64/host-signal.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We do not support sparc32 as a host, so there's no point in sparc64 redirecting to sparc. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- linux-user/include/host/sparc/host-signal.h | 71 ------------------- linux-user/include/host/sparc64/host-signal.h | 64 ++++++++++++++++- 2 files changed, 63 insertions(+), 72 deletions(-) delete mode 100644 linux-user/include/host/sparc/host-signal.h diff --git a/linux-user/include/host/sparc/host-signal.h b/linux-user/include/host/sparc/host-signal.h deleted file mode 100644 index 871b6bb269..0000000000 --- a/linux-user/include/host/sparc/host-signal.h +++ /dev/null @@ -1,71 +0,0 @@ -/* - * host-signal.h: signal info dependent on the host architecture - * - * Copyright (c) 2003-2005 Fabrice Bellard - * Copyright (c) 2021 Linaro Limited - * - * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. - * See the COPYING file in the top-level directory. - */ - -#ifndef SPARC_HOST_SIGNAL_H -#define SPARC_HOST_SIGNAL_H - -/* FIXME: the third argument to a SA_SIGINFO handler is *not* ucontext_t. */ -typedef ucontext_t host_sigcontext; - -static inline uintptr_t host_signal_pc(host_sigcontext *uc) -{ -#ifdef __arch64__ - return uc->uc_mcontext.mc_gregs[MC_PC]; -#else - return uc->uc_mcontext.gregs[REG_PC]; -#endif -} - -static inline void host_signal_set_pc(host_sigcontext *uc, uintptr_t pc) -{ -#ifdef __arch64__ - uc->uc_mcontext.mc_gregs[MC_PC] = pc; -#else - uc->uc_mcontext.gregs[REG_PC] = pc; -#endif -} - -static inline void *host_signal_mask(host_sigcontext *uc) -{ - return &uc->uc_sigmask; -} - -static inline bool host_signal_write(siginfo_t *info, host_sigcontext *uc) -{ - uint32_t insn = *(uint32_t *)host_signal_pc(uc); - - if ((insn >> 30) == 3) { - switch ((insn >> 19) & 0x3f) { - case 0x05: /* stb */ - case 0x15: /* stba */ - case 0x06: /* sth */ - case 0x16: /* stha */ - case 0x04: /* st */ - case 0x14: /* sta */ - case 0x07: /* std */ - case 0x17: /* stda */ - case 0x0e: /* stx */ - case 0x1e: /* stxa */ - case 0x24: /* stf */ - case 0x34: /* stfa */ - case 0x27: /* stdf */ - case 0x37: /* stdfa */ - case 0x26: /* stqf */ - case 0x36: /* stqfa */ - case 0x25: /* stfsr */ - case 0x3c: /* casa */ - case 0x3e: /* casxa */ - return true; - } - } - return false; -} - -#endif diff --git a/linux-user/include/host/sparc64/host-signal.h b/linux-user/include/host/sparc64/host-signal.h index 1191fe2d40..f8a8a4908d 100644 --- a/linux-user/include/host/sparc64/host-signal.h +++ b/linux-user/include/host/sparc64/host-signal.h @@ -1 +1,63 @@ -#include "../sparc/host-signal.h" +/* + * host-signal.h: signal info dependent on the host architecture + * + * Copyright (c) 2003-2005 Fabrice Bellard + * Copyright (c) 2021 Linaro Limited + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef SPARC64_HOST_SIGNAL_H +#define SPARC64_HOST_SIGNAL_H + +/* FIXME: the third argument to a SA_SIGINFO handler is *not* ucontext_t. */ +typedef ucontext_t host_sigcontext; + +static inline uintptr_t host_signal_pc(host_sigcontext *uc) +{ + return uc->uc_mcontext.mc_gregs[MC_PC]; +} + +static inline void host_signal_set_pc(host_sigcontext *uc, uintptr_t pc) +{ + uc->uc_mcontext.mc_gregs[MC_PC] = pc; +} + +static inline void *host_signal_mask(host_sigcontext *uc) +{ + return &uc->uc_sigmask; +} + +static inline bool host_signal_write(siginfo_t *info, host_sigcontext *uc) +{ + uint32_t insn = *(uint32_t *)host_signal_pc(uc); + + if ((insn >> 30) == 3) { + switch ((insn >> 19) & 0x3f) { + case 0x05: /* stb */ + case 0x15: /* stba */ + case 0x06: /* sth */ + case 0x16: /* stha */ + case 0x04: /* st */ + case 0x14: /* sta */ + case 0x07: /* std */ + case 0x17: /* stda */ + case 0x0e: /* stx */ + case 0x1e: /* stxa */ + case 0x24: /* stf */ + case 0x34: /* stfa */ + case 0x27: /* stdf */ + case 0x37: /* stdfa */ + case 0x26: /* stqf */ + case 0x36: /* stqfa */ + case 0x25: /* stfsr */ + case 0x3c: /* casa */ + case 0x3e: /* casxa */ + return true; + } + } + return false; +} + +#endif From 4f152ef27e26d82905244b7cbe344929630d8fae Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 8 Feb 2022 09:49:12 +0300 Subject: [PATCH 299/460] linux-user/include/host/sparc64: Fix host_sigcontext Sparc64 is unique on linux in *not* passing ucontext_t as the third argument to a SA_SIGINFO handler. It passes the old struct sigcontext instead. Set both pc and npc in host_signal_set_pc. Fixes: 8b5bd461935b ("linux-user/host/sparc: Populate host_signal.h") Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/include/host/sparc64/host-signal.h | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/linux-user/include/host/sparc64/host-signal.h b/linux-user/include/host/sparc64/host-signal.h index f8a8a4908d..64957c2bca 100644 --- a/linux-user/include/host/sparc64/host-signal.h +++ b/linux-user/include/host/sparc64/host-signal.h @@ -11,22 +11,23 @@ #ifndef SPARC64_HOST_SIGNAL_H #define SPARC64_HOST_SIGNAL_H -/* FIXME: the third argument to a SA_SIGINFO handler is *not* ucontext_t. */ -typedef ucontext_t host_sigcontext; +/* The third argument to a SA_SIGINFO handler is struct sigcontext. */ +typedef struct sigcontext host_sigcontext; -static inline uintptr_t host_signal_pc(host_sigcontext *uc) +static inline uintptr_t host_signal_pc(host_sigcontext *sc) { - return uc->uc_mcontext.mc_gregs[MC_PC]; + return sc->sigc_regs.tpc; } -static inline void host_signal_set_pc(host_sigcontext *uc, uintptr_t pc) +static inline void host_signal_set_pc(host_sigcontext *sc, uintptr_t pc) { - uc->uc_mcontext.mc_gregs[MC_PC] = pc; + sc->sigc_regs.tpc = pc; + sc->sigc_regs.tnpc = pc + 4; } -static inline void *host_signal_mask(host_sigcontext *uc) +static inline void *host_signal_mask(host_sigcontext *sc) { - return &uc->uc_sigmask; + return &sc->sigc_mask; } static inline bool host_signal_write(siginfo_t *info, host_sigcontext *uc) From cfc2a2d69d59f02b32df3098ce17e10ab86d43c6 Mon Sep 17 00:00:00 2001 From: Idan Horowitz Date: Mon, 10 Jan 2022 18:47:53 +0200 Subject: [PATCH 300/460] accel/tcg: Optimize jump cache flush during tlb range flush When the length of the range is large enough, clearing the whole cache is faster than iterating over the (possibly extremely large) set of pages contained in the range. This mimics the pre-existing similar optimization done on the flush of the tlb itself. Signed-off-by: Idan Horowitz Message-Id: <20220110164754.1066025-1-idan.horowitz@gmail.com> Reviewed-by: Richard Henderson Signed-off-by: Richard Henderson --- accel/tcg/cputlb.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index 5e0d0eebc3..926d9a9192 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -783,6 +783,15 @@ static void tlb_flush_range_by_mmuidx_async_0(CPUState *cpu, } qemu_spin_unlock(&env_tlb(env)->c.lock); + /* + * If the length is larger than the jump cache size, then it will take + * longer to clear each entry individually than it will to clear it all. + */ + if (d.len >= (TARGET_PAGE_SIZE * TB_JMP_CACHE_SIZE)) { + cpu_tb_jmp_cache_clear(cpu); + return; + } + for (target_ulong i = 0; i < d.len; i += TARGET_PAGE_SIZE) { tb_flush_jmp_cache(cpu, d.addr + i); } From 25e82fb769eddb83e0b68487b8b08d9426704d54 Mon Sep 17 00:00:00 2001 From: Idan Horowitz Date: Fri, 14 Jan 2022 02:43:57 +0200 Subject: [PATCH 301/460] softmmu/cpus: Check if the cpu work list is empty atomically Instead of taking the lock of the cpu work list in order to check if it's empty, we can just read the head pointer atomically. This decreases cpu_work_list_empty's share from 5% to 1.3% in a profile of icount-enabled aarch64-softmmu. Signed-off-by: Idan Horowitz Message-Id: <20220114004358.299534-1-idan.horowitz@gmail.com> Reviewed-by: Richard Henderson Signed-off-by: Richard Henderson --- softmmu/cpus.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/softmmu/cpus.c b/softmmu/cpus.c index 23bca46b07..035395ae13 100644 --- a/softmmu/cpus.c +++ b/softmmu/cpus.c @@ -73,12 +73,7 @@ bool cpu_is_stopped(CPUState *cpu) bool cpu_work_list_empty(CPUState *cpu) { - bool ret; - - qemu_mutex_lock(&cpu->work_mutex); - ret = QSIMPLEQ_EMPTY(&cpu->work_list); - qemu_mutex_unlock(&cpu->work_mutex); - return ret; + return QSIMPLEQ_EMPTY_ATOMIC(&cpu->work_list); } bool cpu_thread_is_idle(CPUState *cpu) From c3e97f640642d6ea11cddc1ed9d4d68d180eb18a Mon Sep 17 00:00:00 2001 From: Pavel Dovgalyuk Date: Mon, 31 Jan 2022 14:25:40 +0300 Subject: [PATCH 302/460] replay: use CF_NOIRQ for special exception-replaying TB Commit aff0e204cb1f1c036a496c94c15f5dfafcd9b4b4 introduced CF_NOIRQ usage, but one case was forgotten. Record/replay uses one special TB which is not really executed, but used to cause a correct exception in replay mode. This patch adds CF_NOIRQ flag for such block. Signed-off-by: Pavel Dovgalyuk Reviewed-by: Richard Henderson Message-Id: <164362834054.1754532.7678416881159817273.stgit@pasha-ThinkPad-X280> Signed-off-by: Richard Henderson --- accel/tcg/cpu-exec.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index 8b4cd6c59d..8da6a55593 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -648,7 +648,8 @@ static inline bool cpu_handle_exception(CPUState *cpu, int *ret) if (replay_has_exception() && cpu_neg(cpu)->icount_decr.u16.low + cpu->icount_extra == 0) { /* Execute just one insn to trigger exception pending in the log */ - cpu->cflags_next_tb = (curr_cflags(cpu) & ~CF_USE_ICOUNT) | 1; + cpu->cflags_next_tb = (curr_cflags(cpu) & ~CF_USE_ICOUNT) + | CF_NOIRQ | 1; } #endif return false; From 7b17a475404c970de1b192d026e00058cea980a6 Mon Sep 17 00:00:00 2001 From: WANG Xuerui Date: Mon, 7 Feb 2022 00:21:06 +0800 Subject: [PATCH 303/460] tcg/loongarch64: Fix fallout from recent MO_Q renaming Apparently we were left behind; just renaming MO_Q to MO_UQ is enough. Fixes: fc313c64345453c7 ("exec/memop: Adding signedness to quad definitions") Signed-off-by: WANG Xuerui Message-Id: <20220206162106.1092364-1-i.qemu@xen0n.name> Signed-off-by: Richard Henderson --- tcg/loongarch64/tcg-target.c.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index 9cd46c9be3..d31a0e5991 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -871,7 +871,7 @@ static void tcg_out_qemu_ld_indexed(TCGContext *s, TCGReg rd, TCGReg rj, case MO_SL: tcg_out_opc_ldx_w(s, rd, rj, rk); break; - case MO_Q: + case MO_UQ: tcg_out_opc_ldx_d(s, rd, rj, rk); break; default: From b1ee3c67253a40f8e62a799eb5b5a0d2954769f4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 27 Jul 2021 19:42:35 -1000 Subject: [PATCH 304/460] tcg/i386: Support raising sigbus for user-only Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- tcg/i386/tcg-target.c.inc | 103 ++++++++++++++++++++++++++++++++++++-- tcg/i386/tcg-target.h | 2 - 2 files changed, 98 insertions(+), 7 deletions(-) diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index 4dab09f265..faa15eecab 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -22,6 +22,7 @@ * THE SOFTWARE. */ +#include "../tcg-ldst.c.inc" #include "../tcg-pool.c.inc" #ifdef CONFIG_DEBUG_TCG @@ -421,8 +422,9 @@ static bool tcg_target_const_match(int64_t val, TCGType type, int ct) #define OPC_VZEROUPPER (0x77 | P_EXT) #define OPC_XCHG_ax_r32 (0x90) -#define OPC_GRP3_Ev (0xf7) -#define OPC_GRP5 (0xff) +#define OPC_GRP3_Eb (0xf6) +#define OPC_GRP3_Ev (0xf7) +#define OPC_GRP5 (0xff) #define OPC_GRP14 (0x73 | P_EXT | P_DATA16) /* Group 1 opcode extensions for 0x80-0x83. @@ -444,6 +446,7 @@ static bool tcg_target_const_match(int64_t val, TCGType type, int ct) #define SHIFT_SAR 7 /* Group 3 opcode extensions for 0xf6, 0xf7. To be used with OPC_GRP3. */ +#define EXT3_TESTi 0 #define EXT3_NOT 2 #define EXT3_NEG 3 #define EXT3_MUL 4 @@ -1606,8 +1609,6 @@ static void tcg_out_nopn(TCGContext *s, int n) } #if defined(CONFIG_SOFTMMU) -#include "../tcg-ldst.c.inc" - /* helper signature: helper_ret_ld_mmu(CPUState *env, target_ulong addr, * int mmu_idx, uintptr_t ra) */ @@ -1916,7 +1917,84 @@ static bool tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *l) tcg_out_jmp(s, qemu_st_helpers[opc & (MO_BSWAP | MO_SIZE)]); return true; } -#elif TCG_TARGET_REG_BITS == 32 +#else + +static void tcg_out_test_alignment(TCGContext *s, bool is_ld, TCGReg addrlo, + TCGReg addrhi, unsigned a_bits) +{ + unsigned a_mask = (1 << a_bits) - 1; + TCGLabelQemuLdst *label; + + /* + * We are expecting a_bits to max out at 7, so we can usually use testb. + * For i686, we have to use testl for %esi/%edi. + */ + if (a_mask <= 0xff && (TCG_TARGET_REG_BITS == 64 || addrlo < 4)) { + tcg_out_modrm(s, OPC_GRP3_Eb | P_REXB_RM, EXT3_TESTi, addrlo); + tcg_out8(s, a_mask); + } else { + tcg_out_modrm(s, OPC_GRP3_Ev, EXT3_TESTi, addrlo); + tcg_out32(s, a_mask); + } + + /* jne slow_path */ + tcg_out_opc(s, OPC_JCC_long + JCC_JNE, 0, 0, 0); + + label = new_ldst_label(s); + label->is_ld = is_ld; + label->addrlo_reg = addrlo; + label->addrhi_reg = addrhi; + label->raddr = tcg_splitwx_to_rx(s->code_ptr + 4); + label->label_ptr[0] = s->code_ptr; + + s->code_ptr += 4; +} + +static bool tcg_out_fail_alignment(TCGContext *s, TCGLabelQemuLdst *l) +{ + /* resolve label address */ + tcg_patch32(l->label_ptr[0], s->code_ptr - l->label_ptr[0] - 4); + + if (TCG_TARGET_REG_BITS == 32) { + int ofs = 0; + + tcg_out_st(s, TCG_TYPE_PTR, TCG_AREG0, TCG_REG_ESP, ofs); + ofs += 4; + + tcg_out_st(s, TCG_TYPE_I32, l->addrlo_reg, TCG_REG_ESP, ofs); + ofs += 4; + if (TARGET_LONG_BITS == 64) { + tcg_out_st(s, TCG_TYPE_I32, l->addrhi_reg, TCG_REG_ESP, ofs); + ofs += 4; + } + + tcg_out_pushi(s, (uintptr_t)l->raddr); + } else { + tcg_out_mov(s, TCG_TYPE_TL, tcg_target_call_iarg_regs[1], + l->addrlo_reg); + tcg_out_mov(s, TCG_TYPE_PTR, tcg_target_call_iarg_regs[0], TCG_AREG0); + + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_RAX, (uintptr_t)l->raddr); + tcg_out_push(s, TCG_REG_RAX); + } + + /* "Tail call" to the helper, with the return address back inline. */ + tcg_out_jmp(s, (const void *)(l->is_ld ? helper_unaligned_ld + : helper_unaligned_st)); + return true; +} + +static bool tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *l) +{ + return tcg_out_fail_alignment(s, l); +} + +static bool tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *l) +{ + return tcg_out_fail_alignment(s, l); +} + +#if TCG_TARGET_REG_BITS == 32 # define x86_guest_base_seg 0 # define x86_guest_base_index -1 # define x86_guest_base_offset guest_base @@ -1950,6 +2028,7 @@ static inline int setup_guest_base_seg(void) return 0; } # endif +#endif #endif /* SOFTMMU */ static void tcg_out_qemu_ld_direct(TCGContext *s, TCGReg datalo, TCGReg datahi, @@ -2059,6 +2138,8 @@ static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, bool is64) #if defined(CONFIG_SOFTMMU) int mem_index; tcg_insn_unit *label_ptr[2]; +#else + unsigned a_bits; #endif datalo = *args++; @@ -2081,6 +2162,11 @@ static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, bool is64) add_qemu_ldst_label(s, true, is64, oi, datalo, datahi, addrlo, addrhi, s->code_ptr, label_ptr); #else + a_bits = get_alignment_bits(opc); + if (a_bits) { + tcg_out_test_alignment(s, true, addrlo, addrhi, a_bits); + } + tcg_out_qemu_ld_direct(s, datalo, datahi, addrlo, x86_guest_base_index, x86_guest_base_offset, x86_guest_base_seg, is64, opc); @@ -2148,6 +2234,8 @@ static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args, bool is64) #if defined(CONFIG_SOFTMMU) int mem_index; tcg_insn_unit *label_ptr[2]; +#else + unsigned a_bits; #endif datalo = *args++; @@ -2170,6 +2258,11 @@ static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args, bool is64) add_qemu_ldst_label(s, false, is64, oi, datalo, datahi, addrlo, addrhi, s->code_ptr, label_ptr); #else + a_bits = get_alignment_bits(opc); + if (a_bits) { + tcg_out_test_alignment(s, false, addrlo, addrhi, a_bits); + } + tcg_out_qemu_st_direct(s, datalo, datahi, addrlo, x86_guest_base_index, x86_guest_base_offset, x86_guest_base_seg, opc); #endif diff --git a/tcg/i386/tcg-target.h b/tcg/i386/tcg-target.h index b00a6da293..3b2c9437a0 100644 --- a/tcg/i386/tcg-target.h +++ b/tcg/i386/tcg-target.h @@ -232,9 +232,7 @@ static inline void tb_target_set_jmp_target(uintptr_t tc_ptr, uintptr_t jmp_rx, #define TCG_TARGET_HAS_MEMORY_BSWAP have_movbe -#ifdef CONFIG_SOFTMMU #define TCG_TARGET_NEED_LDST_LABELS -#endif #define TCG_TARGET_NEED_POOL_LABELS #endif From f85ab3d2e51e45e7fa33b539bc7e1350bdf64dde Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 3 Aug 2021 20:07:57 +0000 Subject: [PATCH 305/460] tcg/aarch64: Support raising sigbus for user-only Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- tcg/aarch64/tcg-target.c.inc | 91 +++++++++++++++++++++++++++++------- tcg/aarch64/tcg-target.h | 2 - 2 files changed, 74 insertions(+), 19 deletions(-) diff --git a/tcg/aarch64/tcg-target.c.inc b/tcg/aarch64/tcg-target.c.inc index a8db553287..077fc51401 100644 --- a/tcg/aarch64/tcg-target.c.inc +++ b/tcg/aarch64/tcg-target.c.inc @@ -10,6 +10,7 @@ * See the COPYING file in the top-level directory for details. */ +#include "../tcg-ldst.c.inc" #include "../tcg-pool.c.inc" #include "qemu/bitops.h" @@ -443,6 +444,7 @@ typedef enum { I3404_ANDI = 0x12000000, I3404_ORRI = 0x32000000, I3404_EORI = 0x52000000, + I3404_ANDSI = 0x72000000, /* Move wide immediate instructions. */ I3405_MOVN = 0x12800000, @@ -1328,8 +1330,9 @@ static void tcg_out_goto_long(TCGContext *s, const tcg_insn_unit *target) if (offset == sextract64(offset, 0, 26)) { tcg_out_insn(s, 3206, B, offset); } else { - tcg_out_movi(s, TCG_TYPE_I64, TCG_REG_TMP, (intptr_t)target); - tcg_out_insn(s, 3207, BR, TCG_REG_TMP); + /* Choose X9 as a call-clobbered non-LR temporary. */ + tcg_out_movi(s, TCG_TYPE_I64, TCG_REG_X9, (intptr_t)target); + tcg_out_insn(s, 3207, BR, TCG_REG_X9); } } @@ -1541,9 +1544,14 @@ static void tcg_out_cltz(TCGContext *s, TCGType ext, TCGReg d, } } -#ifdef CONFIG_SOFTMMU -#include "../tcg-ldst.c.inc" +static void tcg_out_adr(TCGContext *s, TCGReg rd, const void *target) +{ + ptrdiff_t offset = tcg_pcrel_diff(s, target); + tcg_debug_assert(offset == sextract64(offset, 0, 21)); + tcg_out_insn(s, 3406, ADR, rd, offset); +} +#ifdef CONFIG_SOFTMMU /* helper signature: helper_ret_ld_mmu(CPUState *env, target_ulong addr, * MemOpIdx oi, uintptr_t ra) */ @@ -1577,13 +1585,6 @@ static void * const qemu_st_helpers[MO_SIZE + 1] = { #endif }; -static inline void tcg_out_adr(TCGContext *s, TCGReg rd, const void *target) -{ - ptrdiff_t offset = tcg_pcrel_diff(s, target); - tcg_debug_assert(offset == sextract64(offset, 0, 21)); - tcg_out_insn(s, 3406, ADR, rd, offset); -} - static bool tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *lb) { MemOpIdx oi = lb->oi; @@ -1714,15 +1715,58 @@ static void tcg_out_tlb_read(TCGContext *s, TCGReg addr_reg, MemOp opc, tcg_out_insn(s, 3202, B_C, TCG_COND_NE, 0); } +#else +static void tcg_out_test_alignment(TCGContext *s, bool is_ld, TCGReg addr_reg, + unsigned a_bits) +{ + unsigned a_mask = (1 << a_bits) - 1; + TCGLabelQemuLdst *label = new_ldst_label(s); + + label->is_ld = is_ld; + label->addrlo_reg = addr_reg; + + /* tst addr, #mask */ + tcg_out_logicali(s, I3404_ANDSI, 0, TCG_REG_XZR, addr_reg, a_mask); + + label->label_ptr[0] = s->code_ptr; + + /* b.ne slow_path */ + tcg_out_insn(s, 3202, B_C, TCG_COND_NE, 0); + + label->raddr = tcg_splitwx_to_rx(s->code_ptr); +} + +static bool tcg_out_fail_alignment(TCGContext *s, TCGLabelQemuLdst *l) +{ + if (!reloc_pc19(l->label_ptr[0], tcg_splitwx_to_rx(s->code_ptr))) { + return false; + } + + tcg_out_mov(s, TCG_TYPE_TL, TCG_REG_X1, l->addrlo_reg); + tcg_out_mov(s, TCG_TYPE_PTR, TCG_REG_X0, TCG_AREG0); + + /* "Tail call" to the helper, with the return address back inline. */ + tcg_out_adr(s, TCG_REG_LR, l->raddr); + tcg_out_goto_long(s, (const void *)(l->is_ld ? helper_unaligned_ld + : helper_unaligned_st)); + return true; +} + +static bool tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *l) +{ + return tcg_out_fail_alignment(s, l); +} + +static bool tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *l) +{ + return tcg_out_fail_alignment(s, l); +} #endif /* CONFIG_SOFTMMU */ static void tcg_out_qemu_ld_direct(TCGContext *s, MemOp memop, TCGType ext, TCGReg data_r, TCGReg addr_r, TCGType otype, TCGReg off_r) { - /* Byte swapping is left to middle-end expansion. */ - tcg_debug_assert((memop & MO_BSWAP) == 0); - switch (memop & MO_SSIZE) { case MO_UB: tcg_out_ldst_r(s, I3312_LDRB, data_r, addr_r, otype, off_r); @@ -1756,9 +1800,6 @@ static void tcg_out_qemu_st_direct(TCGContext *s, MemOp memop, TCGReg data_r, TCGReg addr_r, TCGType otype, TCGReg off_r) { - /* Byte swapping is left to middle-end expansion. */ - tcg_debug_assert((memop & MO_BSWAP) == 0); - switch (memop & MO_SIZE) { case MO_8: tcg_out_ldst_r(s, I3312_STRB, data_r, addr_r, otype, off_r); @@ -1782,6 +1823,10 @@ static void tcg_out_qemu_ld(TCGContext *s, TCGReg data_reg, TCGReg addr_reg, { MemOp memop = get_memop(oi); const TCGType otype = TARGET_LONG_BITS == 64 ? TCG_TYPE_I64 : TCG_TYPE_I32; + + /* Byte swapping is left to middle-end expansion. */ + tcg_debug_assert((memop & MO_BSWAP) == 0); + #ifdef CONFIG_SOFTMMU unsigned mem_index = get_mmuidx(oi); tcg_insn_unit *label_ptr; @@ -1792,6 +1837,10 @@ static void tcg_out_qemu_ld(TCGContext *s, TCGReg data_reg, TCGReg addr_reg, add_qemu_ldst_label(s, true, oi, ext, data_reg, addr_reg, s->code_ptr, label_ptr); #else /* !CONFIG_SOFTMMU */ + unsigned a_bits = get_alignment_bits(memop); + if (a_bits) { + tcg_out_test_alignment(s, true, addr_reg, a_bits); + } if (USE_GUEST_BASE) { tcg_out_qemu_ld_direct(s, memop, ext, data_reg, TCG_REG_GUEST_BASE, otype, addr_reg); @@ -1807,6 +1856,10 @@ static void tcg_out_qemu_st(TCGContext *s, TCGReg data_reg, TCGReg addr_reg, { MemOp memop = get_memop(oi); const TCGType otype = TARGET_LONG_BITS == 64 ? TCG_TYPE_I64 : TCG_TYPE_I32; + + /* Byte swapping is left to middle-end expansion. */ + tcg_debug_assert((memop & MO_BSWAP) == 0); + #ifdef CONFIG_SOFTMMU unsigned mem_index = get_mmuidx(oi); tcg_insn_unit *label_ptr; @@ -1817,6 +1870,10 @@ static void tcg_out_qemu_st(TCGContext *s, TCGReg data_reg, TCGReg addr_reg, add_qemu_ldst_label(s, false, oi, (memop & MO_SIZE)== MO_64, data_reg, addr_reg, s->code_ptr, label_ptr); #else /* !CONFIG_SOFTMMU */ + unsigned a_bits = get_alignment_bits(memop); + if (a_bits) { + tcg_out_test_alignment(s, false, addr_reg, a_bits); + } if (USE_GUEST_BASE) { tcg_out_qemu_st_direct(s, memop, data_reg, TCG_REG_GUEST_BASE, otype, addr_reg); diff --git a/tcg/aarch64/tcg-target.h b/tcg/aarch64/tcg-target.h index 7a93ac8023..876af589ce 100644 --- a/tcg/aarch64/tcg-target.h +++ b/tcg/aarch64/tcg-target.h @@ -151,9 +151,7 @@ typedef enum { void tb_target_set_jmp_target(uintptr_t, uintptr_t, uintptr_t, uintptr_t); -#ifdef CONFIG_SOFTMMU #define TCG_TARGET_NEED_LDST_LABELS -#endif #define TCG_TARGET_NEED_POOL_LABELS #endif /* AARCH64_TCG_TARGET_H */ From 8605cbcdeedb5f24a20dd1341a7a08bd9f38be71 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 3 Aug 2021 22:08:55 +0000 Subject: [PATCH 306/460] tcg/ppc: Support raising sigbus for user-only Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- tcg/ppc/tcg-target.c.inc | 98 ++++++++++++++++++++++++++++++++++++---- tcg/ppc/tcg-target.h | 2 - 2 files changed, 90 insertions(+), 10 deletions(-) diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc index 9e79a7edee..dea24f23c4 100644 --- a/tcg/ppc/tcg-target.c.inc +++ b/tcg/ppc/tcg-target.c.inc @@ -24,6 +24,7 @@ #include "elf.h" #include "../tcg-pool.c.inc" +#include "../tcg-ldst.c.inc" /* * Standardize on the _CALL_FOO symbols used by GCC: @@ -1881,7 +1882,8 @@ void tb_target_set_jmp_target(uintptr_t tc_ptr, uintptr_t jmp_rx, } } -static void tcg_out_call(TCGContext *s, const tcg_insn_unit *target) +static void tcg_out_call_int(TCGContext *s, int lk, + const tcg_insn_unit *target) { #ifdef _CALL_AIX /* Look through the descriptor. If the branch is in range, and we @@ -1892,7 +1894,7 @@ static void tcg_out_call(TCGContext *s, const tcg_insn_unit *target) if (in_range_b(diff) && toc == (uint32_t)toc) { tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_TMP1, toc); - tcg_out_b(s, LK, tgt); + tcg_out_b(s, lk, tgt); } else { /* Fold the low bits of the constant into the addresses below. */ intptr_t arg = (intptr_t)target; @@ -1907,7 +1909,7 @@ static void tcg_out_call(TCGContext *s, const tcg_insn_unit *target) tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_R0, TCG_REG_TMP1, ofs); tcg_out32(s, MTSPR | RA(TCG_REG_R0) | CTR); tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_R2, TCG_REG_TMP1, ofs + SZP); - tcg_out32(s, BCCTR | BO_ALWAYS | LK); + tcg_out32(s, BCCTR | BO_ALWAYS | lk); } #elif defined(_CALL_ELF) && _CALL_ELF == 2 intptr_t diff; @@ -1921,16 +1923,21 @@ static void tcg_out_call(TCGContext *s, const tcg_insn_unit *target) diff = tcg_pcrel_diff(s, target); if (in_range_b(diff)) { - tcg_out_b(s, LK, target); + tcg_out_b(s, lk, target); } else { tcg_out32(s, MTSPR | RS(TCG_REG_R12) | CTR); - tcg_out32(s, BCCTR | BO_ALWAYS | LK); + tcg_out32(s, BCCTR | BO_ALWAYS | lk); } #else - tcg_out_b(s, LK, target); + tcg_out_b(s, lk, target); #endif } +static void tcg_out_call(TCGContext *s, const tcg_insn_unit *target) +{ + tcg_out_call_int(s, LK, target); +} + static const uint32_t qemu_ldx_opc[(MO_SSIZE + MO_BSWAP) + 1] = { [MO_UB] = LBZX, [MO_UW] = LHZX, @@ -1960,8 +1967,6 @@ static const uint32_t qemu_exts_opc[4] = { }; #if defined (CONFIG_SOFTMMU) -#include "../tcg-ldst.c.inc" - /* helper signature: helper_ld_mmu(CPUState *env, target_ulong addr, * int mmu_idx, uintptr_t ra) */ @@ -2227,6 +2232,71 @@ static bool tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *lb) tcg_out_b(s, 0, lb->raddr); return true; } +#else + +static void tcg_out_test_alignment(TCGContext *s, bool is_ld, TCGReg addrlo, + TCGReg addrhi, unsigned a_bits) +{ + unsigned a_mask = (1 << a_bits) - 1; + TCGLabelQemuLdst *label = new_ldst_label(s); + + label->is_ld = is_ld; + label->addrlo_reg = addrlo; + label->addrhi_reg = addrhi; + + /* We are expecting a_bits to max out at 7, much lower than ANDI. */ + tcg_debug_assert(a_bits < 16); + tcg_out32(s, ANDI | SAI(addrlo, TCG_REG_R0, a_mask)); + + label->label_ptr[0] = s->code_ptr; + tcg_out32(s, BC | BI(0, CR_EQ) | BO_COND_FALSE | LK); + + label->raddr = tcg_splitwx_to_rx(s->code_ptr); +} + +static bool tcg_out_fail_alignment(TCGContext *s, TCGLabelQemuLdst *l) +{ + if (!reloc_pc14(l->label_ptr[0], tcg_splitwx_to_rx(s->code_ptr))) { + return false; + } + + if (TCG_TARGET_REG_BITS < TARGET_LONG_BITS) { + TCGReg arg = TCG_REG_R4; +#ifdef TCG_TARGET_CALL_ALIGN_ARGS + arg |= 1; +#endif + if (l->addrlo_reg != arg) { + tcg_out_mov(s, TCG_TYPE_I32, arg, l->addrhi_reg); + tcg_out_mov(s, TCG_TYPE_I32, arg + 1, l->addrlo_reg); + } else if (l->addrhi_reg != arg + 1) { + tcg_out_mov(s, TCG_TYPE_I32, arg + 1, l->addrlo_reg); + tcg_out_mov(s, TCG_TYPE_I32, arg, l->addrhi_reg); + } else { + tcg_out_mov(s, TCG_TYPE_I32, TCG_REG_R0, arg); + tcg_out_mov(s, TCG_TYPE_I32, arg, arg + 1); + tcg_out_mov(s, TCG_TYPE_I32, arg + 1, TCG_REG_R0); + } + } else { + tcg_out_mov(s, TCG_TYPE_TL, TCG_REG_R4, l->addrlo_reg); + } + tcg_out_mov(s, TCG_TYPE_TL, TCG_REG_R3, TCG_AREG0); + + /* "Tail call" to the helper, with the return address back inline. */ + tcg_out_call_int(s, 0, (const void *)(l->is_ld ? helper_unaligned_ld + : helper_unaligned_st)); + return true; +} + +static bool tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *l) +{ + return tcg_out_fail_alignment(s, l); +} + +static bool tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *l) +{ + return tcg_out_fail_alignment(s, l); +} + #endif /* SOFTMMU */ static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, bool is_64) @@ -2238,6 +2308,8 @@ static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, bool is_64) #ifdef CONFIG_SOFTMMU int mem_index; tcg_insn_unit *label_ptr; +#else + unsigned a_bits; #endif datalo = *args++; @@ -2258,6 +2330,10 @@ static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, bool is_64) rbase = TCG_REG_R3; #else /* !CONFIG_SOFTMMU */ + a_bits = get_alignment_bits(opc); + if (a_bits) { + tcg_out_test_alignment(s, true, addrlo, addrhi, a_bits); + } rbase = guest_base ? TCG_GUEST_BASE_REG : 0; if (TCG_TARGET_REG_BITS > TARGET_LONG_BITS) { tcg_out_ext32u(s, TCG_REG_TMP1, addrlo); @@ -2313,6 +2389,8 @@ static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args, bool is_64) #ifdef CONFIG_SOFTMMU int mem_index; tcg_insn_unit *label_ptr; +#else + unsigned a_bits; #endif datalo = *args++; @@ -2333,6 +2411,10 @@ static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args, bool is_64) rbase = TCG_REG_R3; #else /* !CONFIG_SOFTMMU */ + a_bits = get_alignment_bits(opc); + if (a_bits) { + tcg_out_test_alignment(s, false, addrlo, addrhi, a_bits); + } rbase = guest_base ? TCG_GUEST_BASE_REG : 0; if (TCG_TARGET_REG_BITS > TARGET_LONG_BITS) { tcg_out_ext32u(s, TCG_REG_TMP1, addrlo); diff --git a/tcg/ppc/tcg-target.h b/tcg/ppc/tcg-target.h index 0943192cde..c775c97b61 100644 --- a/tcg/ppc/tcg-target.h +++ b/tcg/ppc/tcg-target.h @@ -182,9 +182,7 @@ void tb_target_set_jmp_target(uintptr_t, uintptr_t, uintptr_t, uintptr_t); #define TCG_TARGET_DEFAULT_MO (0) #define TCG_TARGET_HAS_MEMORY_BSWAP 1 -#ifdef CONFIG_SOFTMMU #define TCG_TARGET_NEED_LDST_LABELS -#endif #define TCG_TARGET_NEED_POOL_LABELS #endif From a3fb7c99c05659c98f4de301a932c70991382e26 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 5 Aug 2021 07:20:12 -1000 Subject: [PATCH 307/460] tcg/riscv: Support raising sigbus for user-only Signed-off-by: Richard Henderson --- tcg/riscv/tcg-target.c.inc | 63 ++++++++++++++++++++++++++++++++++++-- tcg/riscv/tcg-target.h | 2 -- 2 files changed, 61 insertions(+), 4 deletions(-) diff --git a/tcg/riscv/tcg-target.c.inc b/tcg/riscv/tcg-target.c.inc index e9488f7093..6409d9c3d5 100644 --- a/tcg/riscv/tcg-target.c.inc +++ b/tcg/riscv/tcg-target.c.inc @@ -27,6 +27,7 @@ * THE SOFTWARE. */ +#include "../tcg-ldst.c.inc" #include "../tcg-pool.c.inc" #ifdef CONFIG_DEBUG_TCG @@ -847,8 +848,6 @@ static void tcg_out_mb(TCGContext *s, TCGArg a0) */ #if defined(CONFIG_SOFTMMU) -#include "../tcg-ldst.c.inc" - /* helper signature: helper_ret_ld_mmu(CPUState *env, target_ulong addr, * MemOpIdx oi, uintptr_t ra) */ @@ -1053,6 +1052,54 @@ static bool tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *l) tcg_out_goto(s, l->raddr); return true; } +#else + +static void tcg_out_test_alignment(TCGContext *s, bool is_ld, TCGReg addr_reg, + unsigned a_bits) +{ + unsigned a_mask = (1 << a_bits) - 1; + TCGLabelQemuLdst *l = new_ldst_label(s); + + l->is_ld = is_ld; + l->addrlo_reg = addr_reg; + + /* We are expecting a_bits to max out at 7, so we can always use andi. */ + tcg_debug_assert(a_bits < 12); + tcg_out_opc_imm(s, OPC_ANDI, TCG_REG_TMP1, addr_reg, a_mask); + + l->label_ptr[0] = s->code_ptr; + tcg_out_opc_branch(s, OPC_BNE, TCG_REG_TMP1, TCG_REG_ZERO, 0); + + l->raddr = tcg_splitwx_to_rx(s->code_ptr); +} + +static bool tcg_out_fail_alignment(TCGContext *s, TCGLabelQemuLdst *l) +{ + /* resolve label address */ + if (!reloc_sbimm12(l->label_ptr[0], tcg_splitwx_to_rx(s->code_ptr))) { + return false; + } + + tcg_out_mov(s, TCG_TYPE_TL, TCG_REG_A1, l->addrlo_reg); + tcg_out_mov(s, TCG_TYPE_PTR, TCG_REG_A0, TCG_AREG0); + + /* tail call, with the return address back inline. */ + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_RA, (uintptr_t)l->raddr); + tcg_out_call_int(s, (const void *)(l->is_ld ? helper_unaligned_ld + : helper_unaligned_st), true); + return true; +} + +static bool tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *l) +{ + return tcg_out_fail_alignment(s, l); +} + +static bool tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *l) +{ + return tcg_out_fail_alignment(s, l); +} + #endif /* CONFIG_SOFTMMU */ static void tcg_out_qemu_ld_direct(TCGContext *s, TCGReg lo, TCGReg hi, @@ -1108,6 +1155,8 @@ static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, bool is_64) MemOp opc; #if defined(CONFIG_SOFTMMU) tcg_insn_unit *label_ptr[1]; +#else + unsigned a_bits; #endif TCGReg base = TCG_REG_TMP0; @@ -1130,6 +1179,10 @@ static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, bool is_64) tcg_out_ext32u(s, base, addr_regl); addr_regl = base; } + a_bits = get_alignment_bits(opc); + if (a_bits) { + tcg_out_test_alignment(s, true, addr_regl, a_bits); + } if (guest_base != 0) { tcg_out_opc_reg(s, OPC_ADD, base, TCG_GUEST_BASE_REG, addr_regl); } @@ -1174,6 +1227,8 @@ static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args, bool is_64) MemOp opc; #if defined(CONFIG_SOFTMMU) tcg_insn_unit *label_ptr[1]; +#else + unsigned a_bits; #endif TCGReg base = TCG_REG_TMP0; @@ -1196,6 +1251,10 @@ static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args, bool is_64) tcg_out_ext32u(s, base, addr_regl); addr_regl = base; } + a_bits = get_alignment_bits(opc); + if (a_bits) { + tcg_out_test_alignment(s, false, addr_regl, a_bits); + } if (guest_base != 0) { tcg_out_opc_reg(s, OPC_ADD, base, TCG_GUEST_BASE_REG, addr_regl); } diff --git a/tcg/riscv/tcg-target.h b/tcg/riscv/tcg-target.h index ef78b99e98..11c9b3e4f4 100644 --- a/tcg/riscv/tcg-target.h +++ b/tcg/riscv/tcg-target.h @@ -165,9 +165,7 @@ void tb_target_set_jmp_target(uintptr_t, uintptr_t, uintptr_t, uintptr_t); #define TCG_TARGET_DEFAULT_MO (0) -#ifdef CONFIG_SOFTMMU #define TCG_TARGET_NEED_LDST_LABELS -#endif #define TCG_TARGET_NEED_POOL_LABELS #define TCG_TARGET_HAS_MEMORY_BSWAP 0 From 1cd49868d47ceceaa0302582bf024936836392a6 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 4 Aug 2021 00:08:57 +0000 Subject: [PATCH 308/460] tcg/s390x: Support raising sigbus for user-only Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- tcg/s390x/tcg-target.c.inc | 59 ++++++++++++++++++++++++++++++++++++-- tcg/s390x/tcg-target.h | 2 -- 2 files changed, 57 insertions(+), 4 deletions(-) diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index b12fbfda63..d56c1e51e4 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -29,6 +29,7 @@ #error "unsupported code generation mode" #endif +#include "../tcg-ldst.c.inc" #include "../tcg-pool.c.inc" #include "elf.h" @@ -136,6 +137,7 @@ typedef enum S390Opcode { RI_OIHL = 0xa509, RI_OILH = 0xa50a, RI_OILL = 0xa50b, + RI_TMLL = 0xa701, RIE_CGIJ = 0xec7c, RIE_CGRJ = 0xec64, @@ -1804,8 +1806,6 @@ static void tcg_out_qemu_st_direct(TCGContext *s, MemOp opc, TCGReg data, } #if defined(CONFIG_SOFTMMU) -#include "../tcg-ldst.c.inc" - /* We're expecting to use a 20-bit negative offset on the tlb memory ops. */ QEMU_BUILD_BUG_ON(TLB_MASK_TABLE_OFS(0) > 0); QEMU_BUILD_BUG_ON(TLB_MASK_TABLE_OFS(0) < -(1 << 19)); @@ -1942,6 +1942,53 @@ static bool tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *lb) return true; } #else +static void tcg_out_test_alignment(TCGContext *s, bool is_ld, + TCGReg addrlo, unsigned a_bits) +{ + unsigned a_mask = (1 << a_bits) - 1; + TCGLabelQemuLdst *l = new_ldst_label(s); + + l->is_ld = is_ld; + l->addrlo_reg = addrlo; + + /* We are expecting a_bits to max out at 7, much lower than TMLL. */ + tcg_debug_assert(a_bits < 16); + tcg_out_insn(s, RI, TMLL, addrlo, a_mask); + + tcg_out16(s, RI_BRC | (7 << 4)); /* CC in {1,2,3} */ + l->label_ptr[0] = s->code_ptr; + s->code_ptr += 1; + + l->raddr = tcg_splitwx_to_rx(s->code_ptr); +} + +static bool tcg_out_fail_alignment(TCGContext *s, TCGLabelQemuLdst *l) +{ + if (!patch_reloc(l->label_ptr[0], R_390_PC16DBL, + (intptr_t)tcg_splitwx_to_rx(s->code_ptr), 2)) { + return false; + } + + tcg_out_mov(s, TCG_TYPE_TL, TCG_REG_R3, l->addrlo_reg); + tcg_out_mov(s, TCG_TYPE_PTR, TCG_REG_R2, TCG_AREG0); + + /* "Tail call" to the helper, with the return address back inline. */ + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_R14, (uintptr_t)l->raddr); + tgen_gotoi(s, S390_CC_ALWAYS, (const void *)(l->is_ld ? helper_unaligned_ld + : helper_unaligned_st)); + return true; +} + +static bool tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *l) +{ + return tcg_out_fail_alignment(s, l); +} + +static bool tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *l) +{ + return tcg_out_fail_alignment(s, l); +} + static void tcg_prepare_user_ldst(TCGContext *s, TCGReg *addr_reg, TCGReg *index_reg, tcg_target_long *disp) { @@ -1980,7 +2027,11 @@ static void tcg_out_qemu_ld(TCGContext* s, TCGReg data_reg, TCGReg addr_reg, #else TCGReg index_reg; tcg_target_long disp; + unsigned a_bits = get_alignment_bits(opc); + if (a_bits) { + tcg_out_test_alignment(s, true, addr_reg, a_bits); + } tcg_prepare_user_ldst(s, &addr_reg, &index_reg, &disp); tcg_out_qemu_ld_direct(s, opc, data_reg, addr_reg, index_reg, disp); #endif @@ -2007,7 +2058,11 @@ static void tcg_out_qemu_st(TCGContext* s, TCGReg data_reg, TCGReg addr_reg, #else TCGReg index_reg; tcg_target_long disp; + unsigned a_bits = get_alignment_bits(opc); + if (a_bits) { + tcg_out_test_alignment(s, false, addr_reg, a_bits); + } tcg_prepare_user_ldst(s, &addr_reg, &index_reg, &disp); tcg_out_qemu_st_direct(s, opc, data_reg, addr_reg, index_reg, disp); #endif diff --git a/tcg/s390x/tcg-target.h b/tcg/s390x/tcg-target.h index 527ada0f63..69217d995b 100644 --- a/tcg/s390x/tcg-target.h +++ b/tcg/s390x/tcg-target.h @@ -178,9 +178,7 @@ static inline void tb_target_set_jmp_target(uintptr_t tc_ptr, uintptr_t jmp_rx, /* no need to flush icache explicitly */ } -#ifdef CONFIG_SOFTMMU #define TCG_TARGET_NEED_LDST_LABELS -#endif #define TCG_TARGET_NEED_POOL_LABELS #endif From fe1bee3a0a97585e3b59dc1db154c28bc8005882 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 3 Aug 2021 14:56:51 -1000 Subject: [PATCH 309/460] tcg/tci: Support raising sigbus for user-only 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 --- tcg/tci.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/tcg/tci.c b/tcg/tci.c index 336af5945a..fe92b5d084 100644 --- a/tcg/tci.c +++ b/tcg/tci.c @@ -292,11 +292,11 @@ static bool tci_compare64(uint64_t u0, uint64_t u1, TCGCond condition) static uint64_t tci_qemu_ld(CPUArchState *env, target_ulong taddr, MemOpIdx oi, const void *tb_ptr) { - MemOp mop = get_memop(oi) & (MO_BSWAP | MO_SSIZE); + MemOp mop = get_memop(oi); uintptr_t ra = (uintptr_t)tb_ptr; #ifdef CONFIG_SOFTMMU - switch (mop) { + switch (mop & (MO_BSWAP | MO_SSIZE)) { case MO_UB: return helper_ret_ldub_mmu(env, taddr, oi, ra); case MO_SB: @@ -326,10 +326,14 @@ static uint64_t tci_qemu_ld(CPUArchState *env, target_ulong taddr, } #else void *haddr = g2h(env_cpu(env), taddr); + unsigned a_mask = (1u << get_alignment_bits(mop)) - 1; uint64_t ret; set_helper_retaddr(ra); - switch (mop) { + if (taddr & a_mask) { + helper_unaligned_ld(env, taddr); + } + switch (mop & (MO_BSWAP | MO_SSIZE)) { case MO_UB: ret = ldub_p(haddr); break; @@ -377,11 +381,11 @@ static uint64_t tci_qemu_ld(CPUArchState *env, target_ulong taddr, static void tci_qemu_st(CPUArchState *env, target_ulong taddr, uint64_t val, MemOpIdx oi, const void *tb_ptr) { - MemOp mop = get_memop(oi) & (MO_BSWAP | MO_SSIZE); + MemOp mop = get_memop(oi); uintptr_t ra = (uintptr_t)tb_ptr; #ifdef CONFIG_SOFTMMU - switch (mop) { + switch (mop & (MO_BSWAP | MO_SIZE)) { case MO_UB: helper_ret_stb_mmu(env, taddr, val, oi, ra); break; @@ -408,9 +412,13 @@ static void tci_qemu_st(CPUArchState *env, target_ulong taddr, uint64_t val, } #else void *haddr = g2h(env_cpu(env), taddr); + unsigned a_mask = (1u << get_alignment_bits(mop)) - 1; set_helper_retaddr(ra); - switch (mop) { + if (taddr & a_mask) { + helper_unaligned_st(env, taddr); + } + switch (mop & (MO_BSWAP | MO_SIZE)) { case MO_UB: stb_p(haddr, val); break; From 6f78c7b0828660f6733528dab7bedf027d4958f6 Mon Sep 17 00:00:00 2001 From: WANG Xuerui Date: Thu, 6 Jan 2022 21:42:38 +0800 Subject: [PATCH 310/460] tcg/loongarch64: Support raising sigbus for user-only Signed-off-by: WANG Xuerui Reviewed-by: Richard Henderson Message-Id: <20220106134238.3936163-1-git@xen0n.name> Signed-off-by: Richard Henderson --- tcg/loongarch64/tcg-target.c.inc | 71 +++++++++++++++++++++++++++++++- tcg/loongarch64/tcg-target.h | 2 - 2 files changed, 69 insertions(+), 4 deletions(-) diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index d31a0e5991..a3debf6da7 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -29,6 +29,8 @@ * THE SOFTWARE. */ +#include "../tcg-ldst.c.inc" + #ifdef CONFIG_DEBUG_TCG static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = { "zero", @@ -642,8 +644,6 @@ static bool tcg_out_sti(TCGContext *s, TCGType type, TCGArg val, */ #if defined(CONFIG_SOFTMMU) -#include "../tcg-ldst.c.inc" - /* * helper signature: helper_ret_ld_mmu(CPUState *env, target_ulong addr, * MemOpIdx oi, uintptr_t ra) @@ -825,6 +825,61 @@ static bool tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *l) return tcg_out_goto(s, l->raddr); } +#else + +/* + * Alignment helpers for user-mode emulation + */ + +static void tcg_out_test_alignment(TCGContext *s, bool is_ld, TCGReg addr_reg, + unsigned a_bits) +{ + TCGLabelQemuLdst *l = new_ldst_label(s); + + l->is_ld = is_ld; + l->addrlo_reg = addr_reg; + + /* + * Without micro-architecture details, we don't know which of bstrpick or + * andi is faster, so use bstrpick as it's not constrained by imm field + * width. (Not to say alignments >= 2^12 are going to happen any time + * soon, though) + */ + tcg_out_opc_bstrpick_d(s, TCG_REG_TMP1, addr_reg, 0, a_bits - 1); + + l->label_ptr[0] = s->code_ptr; + tcg_out_opc_bne(s, TCG_REG_TMP1, TCG_REG_ZERO, 0); + + l->raddr = tcg_splitwx_to_rx(s->code_ptr); +} + +static bool tcg_out_fail_alignment(TCGContext *s, TCGLabelQemuLdst *l) +{ + /* resolve label address */ + if (!reloc_br_sk16(l->label_ptr[0], tcg_splitwx_to_rx(s->code_ptr))) { + return false; + } + + tcg_out_mov(s, TCG_TYPE_TL, TCG_REG_A1, l->addrlo_reg); + tcg_out_mov(s, TCG_TYPE_PTR, TCG_REG_A0, TCG_AREG0); + + /* tail call, with the return address back inline. */ + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_RA, (uintptr_t)l->raddr); + tcg_out_call_int(s, (const void *)(l->is_ld ? helper_unaligned_ld + : helper_unaligned_st), true); + return true; +} + +static bool tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *l) +{ + return tcg_out_fail_alignment(s, l); +} + +static bool tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *l) +{ + return tcg_out_fail_alignment(s, l); +} + #endif /* CONFIG_SOFTMMU */ /* @@ -887,6 +942,8 @@ static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, TCGType type) MemOp opc; #if defined(CONFIG_SOFTMMU) tcg_insn_unit *label_ptr[1]; +#else + unsigned a_bits; #endif TCGReg base; @@ -903,6 +960,10 @@ static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, TCGType type) data_regl, addr_regl, s->code_ptr, label_ptr); #else + a_bits = get_alignment_bits(opc); + if (a_bits) { + tcg_out_test_alignment(s, true, addr_regl, a_bits); + } base = tcg_out_zext_addr_if_32_bit(s, addr_regl, TCG_REG_TMP0); TCGReg guest_base_reg = USE_GUEST_BASE ? TCG_GUEST_BASE_REG : TCG_REG_ZERO; tcg_out_qemu_ld_indexed(s, data_regl, base, guest_base_reg, opc, type); @@ -941,6 +1002,8 @@ static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args) MemOp opc; #if defined(CONFIG_SOFTMMU) tcg_insn_unit *label_ptr[1]; +#else + unsigned a_bits; #endif TCGReg base; @@ -958,6 +1021,10 @@ static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args) data_regl, addr_regl, s->code_ptr, label_ptr); #else + a_bits = get_alignment_bits(opc); + if (a_bits) { + tcg_out_test_alignment(s, false, addr_regl, a_bits); + } base = tcg_out_zext_addr_if_32_bit(s, addr_regl, TCG_REG_TMP0); TCGReg guest_base_reg = USE_GUEST_BASE ? TCG_GUEST_BASE_REG : TCG_REG_ZERO; tcg_out_qemu_st_indexed(s, data_regl, base, guest_base_reg, opc); diff --git a/tcg/loongarch64/tcg-target.h b/tcg/loongarch64/tcg-target.h index 05010805e7..d58a6162f2 100644 --- a/tcg/loongarch64/tcg-target.h +++ b/tcg/loongarch64/tcg-target.h @@ -171,9 +171,7 @@ void tb_target_set_jmp_target(uintptr_t, uintptr_t, uintptr_t, uintptr_t); #define TCG_TARGET_DEFAULT_MO (0) -#ifdef CONFIG_SOFTMMU #define TCG_TARGET_NEED_LDST_LABELS -#endif #define TCG_TARGET_HAS_MEMORY_BSWAP 0 From 01dfc0ed7f2c5f8dbab65f31228a2888c7b85a07 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 2 Jan 2022 17:42:07 -0800 Subject: [PATCH 311/460] tcg/arm: Drop support for armv4 and armv5 hosts Support for unaligned accesses is difficult for pre-v6 hosts. While debian still builds for armv4, we cannot use a compile time test, so test the architecture at runtime and error out. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- tcg/arm/tcg-target.c.inc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc index 5345c4e39c..29d63e98a8 100644 --- a/tcg/arm/tcg-target.c.inc +++ b/tcg/arm/tcg-target.c.inc @@ -2474,6 +2474,11 @@ static void tcg_target_init(TCGContext *s) if (pl != NULL && pl[0] == 'v' && pl[1] >= '4' && pl[1] <= '9') { arm_arch = pl[1] - '0'; } + + if (arm_arch < 6) { + error_report("TCG: ARMv%d is unsupported; exiting", arm_arch); + exit(EXIT_FAILURE); + } } tcg_target_available_regs[TCG_TYPE_I32] = ALL_GENERAL_REGS; From 6cef13940cc57124bdef9dffbf03f25784f7a51c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 2 Jan 2022 18:30:13 -0800 Subject: [PATCH 312/460] tcg/arm: Remove use_armv5t_instructions This is now always true, since we require armv6. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- tcg/arm/tcg-target.c.inc | 35 ++++++----------------------------- tcg/arm/tcg-target.h | 3 +-- 2 files changed, 7 insertions(+), 31 deletions(-) diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc index 29d63e98a8..f3b635063f 100644 --- a/tcg/arm/tcg-target.c.inc +++ b/tcg/arm/tcg-target.c.inc @@ -596,11 +596,7 @@ static void tcg_out_b_reg(TCGContext *s, ARMCond cond, TCGReg rn) * Unless the C portion of QEMU is compiled as thumb, we don't need * true BX semantics; merely a branch to an address held in a register. */ - if (use_armv5t_instructions) { - tcg_out_bx_reg(s, cond, rn); - } else { - tcg_out_mov_reg(s, cond, TCG_REG_PC, rn); - } + tcg_out_bx_reg(s, cond, rn); } static void tcg_out_dat_imm(TCGContext *s, ARMCond cond, ARMInsn opc, @@ -1247,14 +1243,7 @@ static void tcg_out_goto(TCGContext *s, ARMCond cond, const tcg_insn_unit *addr) } /* LDR is interworking from v5t. */ - if (arm_mode || use_armv5t_instructions) { - tcg_out_movi_pool(s, cond, TCG_REG_PC, addri); - return; - } - - /* else v4t */ - tcg_out_movi32(s, COND_AL, TCG_REG_TMP, addri); - tcg_out_bx_reg(s, COND_AL, TCG_REG_TMP); + tcg_out_movi_pool(s, cond, TCG_REG_PC, addri); } /* @@ -1270,26 +1259,14 @@ static void tcg_out_call(TCGContext *s, const tcg_insn_unit *addr) if (disp - 8 < 0x02000000 && disp - 8 >= -0x02000000) { if (arm_mode) { tcg_out_bl_imm(s, COND_AL, disp); - return; - } - if (use_armv5t_instructions) { + } else { tcg_out_blx_imm(s, disp); - return; } + return; } - if (use_armv5t_instructions) { - tcg_out_movi32(s, COND_AL, TCG_REG_TMP, addri); - tcg_out_blx_reg(s, COND_AL, TCG_REG_TMP); - } else if (arm_mode) { - /* ??? Know that movi_pool emits exactly 1 insn. */ - tcg_out_mov_reg(s, COND_AL, TCG_REG_R14, TCG_REG_PC); - tcg_out_movi_pool(s, COND_AL, TCG_REG_PC, addri); - } else { - tcg_out_movi32(s, COND_AL, TCG_REG_TMP, addri); - tcg_out_mov_reg(s, COND_AL, TCG_REG_R14, TCG_REG_PC); - tcg_out_bx_reg(s, COND_AL, TCG_REG_TMP); - } + tcg_out_movi32(s, COND_AL, TCG_REG_TMP, addri); + tcg_out_blx_reg(s, COND_AL, TCG_REG_TMP); } static void tcg_out_goto_label(TCGContext *s, ARMCond cond, TCGLabel *l) diff --git a/tcg/arm/tcg-target.h b/tcg/arm/tcg-target.h index f41b809554..5c9ba5feea 100644 --- a/tcg/arm/tcg-target.h +++ b/tcg/arm/tcg-target.h @@ -28,7 +28,6 @@ extern int arm_arch; -#define use_armv5t_instructions (__ARM_ARCH >= 5 || arm_arch >= 5) #define use_armv6_instructions (__ARM_ARCH >= 6 || arm_arch >= 6) #define use_armv7_instructions (__ARM_ARCH >= 7 || arm_arch >= 7) @@ -109,7 +108,7 @@ extern bool use_neon_instructions; #define TCG_TARGET_HAS_eqv_i32 0 #define TCG_TARGET_HAS_nand_i32 0 #define TCG_TARGET_HAS_nor_i32 0 -#define TCG_TARGET_HAS_clz_i32 use_armv5t_instructions +#define TCG_TARGET_HAS_clz_i32 1 #define TCG_TARGET_HAS_ctz_i32 use_armv7_instructions #define TCG_TARGET_HAS_ctpop_i32 0 #define TCG_TARGET_HAS_deposit_i32 use_armv7_instructions From bde2cdb59bcd3b0965a80597e72c835fcf5ef7f4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 2 Jan 2022 20:54:57 -0800 Subject: [PATCH 313/460] tcg/arm: Remove use_armv6_instructions This is now always true, since we require armv6. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- tcg/arm/tcg-target.c.inc | 192 ++++++--------------------------------- tcg/arm/tcg-target.h | 1 - 2 files changed, 27 insertions(+), 166 deletions(-) diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc index f3b635063f..9eb43407ea 100644 --- a/tcg/arm/tcg-target.c.inc +++ b/tcg/arm/tcg-target.c.inc @@ -923,17 +923,6 @@ static void tcg_out_dat_rIN(TCGContext *s, ARMCond cond, ARMInsn opc, static void tcg_out_mul32(TCGContext *s, ARMCond cond, TCGReg rd, TCGReg rn, TCGReg rm) { - /* if ArchVersion() < 6 && d == n then UNPREDICTABLE; */ - if (!use_armv6_instructions && rd == rn) { - if (rd == rm) { - /* rd == rn == rm; copy an input to tmp first. */ - tcg_out_mov_reg(s, cond, TCG_REG_TMP, rn); - rm = rn = TCG_REG_TMP; - } else { - rn = rm; - rm = rd; - } - } /* mul */ tcg_out32(s, (cond << 28) | 0x90 | (rd << 16) | (rm << 8) | rn); } @@ -941,17 +930,6 @@ static void tcg_out_mul32(TCGContext *s, ARMCond cond, TCGReg rd, static void tcg_out_umull32(TCGContext *s, ARMCond cond, TCGReg rd0, TCGReg rd1, TCGReg rn, TCGReg rm) { - /* if ArchVersion() < 6 && (dHi == n || dLo == n) then UNPREDICTABLE; */ - if (!use_armv6_instructions && (rd0 == rn || rd1 == rn)) { - if (rd0 == rm || rd1 == rm) { - tcg_out_mov_reg(s, cond, TCG_REG_TMP, rn); - rn = TCG_REG_TMP; - } else { - TCGReg t = rn; - rn = rm; - rm = t; - } - } /* umull */ tcg_out32(s, (cond << 28) | 0x00800090 | (rd1 << 16) | (rd0 << 12) | (rm << 8) | rn); @@ -960,17 +938,6 @@ static void tcg_out_umull32(TCGContext *s, ARMCond cond, TCGReg rd0, static void tcg_out_smull32(TCGContext *s, ARMCond cond, TCGReg rd0, TCGReg rd1, TCGReg rn, TCGReg rm) { - /* if ArchVersion() < 6 && (dHi == n || dLo == n) then UNPREDICTABLE; */ - if (!use_armv6_instructions && (rd0 == rn || rd1 == rn)) { - if (rd0 == rm || rd1 == rm) { - tcg_out_mov_reg(s, cond, TCG_REG_TMP, rn); - rn = TCG_REG_TMP; - } else { - TCGReg t = rn; - rn = rm; - rm = t; - } - } /* smull */ tcg_out32(s, (cond << 28) | 0x00c00090 | (rd1 << 16) | (rd0 << 12) | (rm << 8) | rn); @@ -990,15 +957,8 @@ static void tcg_out_udiv(TCGContext *s, ARMCond cond, static void tcg_out_ext8s(TCGContext *s, ARMCond cond, TCGReg rd, TCGReg rn) { - if (use_armv6_instructions) { - /* sxtb */ - tcg_out32(s, 0x06af0070 | (cond << 28) | (rd << 12) | rn); - } else { - tcg_out_dat_reg(s, cond, ARITH_MOV, - rd, 0, rn, SHIFT_IMM_LSL(24)); - tcg_out_dat_reg(s, cond, ARITH_MOV, - rd, 0, rd, SHIFT_IMM_ASR(24)); - } + /* sxtb */ + tcg_out32(s, 0x06af0070 | (cond << 28) | (rd << 12) | rn); } static void __attribute__((unused)) @@ -1009,113 +969,37 @@ tcg_out_ext8u(TCGContext *s, ARMCond cond, TCGReg rd, TCGReg rn) static void tcg_out_ext16s(TCGContext *s, ARMCond cond, TCGReg rd, TCGReg rn) { - if (use_armv6_instructions) { - /* sxth */ - tcg_out32(s, 0x06bf0070 | (cond << 28) | (rd << 12) | rn); - } else { - tcg_out_dat_reg(s, cond, ARITH_MOV, - rd, 0, rn, SHIFT_IMM_LSL(16)); - tcg_out_dat_reg(s, cond, ARITH_MOV, - rd, 0, rd, SHIFT_IMM_ASR(16)); - } + /* sxth */ + tcg_out32(s, 0x06bf0070 | (cond << 28) | (rd << 12) | rn); } static void tcg_out_ext16u(TCGContext *s, ARMCond cond, TCGReg rd, TCGReg rn) { - if (use_armv6_instructions) { - /* uxth */ - tcg_out32(s, 0x06ff0070 | (cond << 28) | (rd << 12) | rn); - } else { - tcg_out_dat_reg(s, cond, ARITH_MOV, - rd, 0, rn, SHIFT_IMM_LSL(16)); - tcg_out_dat_reg(s, cond, ARITH_MOV, - rd, 0, rd, SHIFT_IMM_LSR(16)); - } + /* uxth */ + tcg_out32(s, 0x06ff0070 | (cond << 28) | (rd << 12) | rn); } static void tcg_out_bswap16(TCGContext *s, ARMCond cond, TCGReg rd, TCGReg rn, int flags) { - if (use_armv6_instructions) { - if (flags & TCG_BSWAP_OS) { - /* revsh */ - tcg_out32(s, 0x06ff0fb0 | (cond << 28) | (rd << 12) | rn); - return; - } - - /* rev16 */ - tcg_out32(s, 0x06bf0fb0 | (cond << 28) | (rd << 12) | rn); - if ((flags & (TCG_BSWAP_IZ | TCG_BSWAP_OZ)) == TCG_BSWAP_OZ) { - /* uxth */ - tcg_out32(s, 0x06ff0070 | (cond << 28) | (rd << 12) | rd); - } + if (flags & TCG_BSWAP_OS) { + /* revsh */ + tcg_out32(s, 0x06ff0fb0 | (cond << 28) | (rd << 12) | rn); return; } - if (flags == 0) { - /* - * For stores, no input or output extension: - * rn = xxAB - * lsr tmp, rn, #8 tmp = 0xxA - * and tmp, tmp, #0xff tmp = 000A - * orr rd, tmp, rn, lsl #8 rd = xABA - */ - tcg_out_dat_reg(s, cond, ARITH_MOV, - TCG_REG_TMP, 0, rn, SHIFT_IMM_LSR(8)); - tcg_out_dat_imm(s, cond, ARITH_AND, TCG_REG_TMP, TCG_REG_TMP, 0xff); - tcg_out_dat_reg(s, cond, ARITH_ORR, - rd, TCG_REG_TMP, rn, SHIFT_IMM_LSL(8)); - return; + /* rev16 */ + tcg_out32(s, 0x06bf0fb0 | (cond << 28) | (rd << 12) | rn); + if ((flags & (TCG_BSWAP_IZ | TCG_BSWAP_OZ)) == TCG_BSWAP_OZ) { + /* uxth */ + tcg_out32(s, 0x06ff0070 | (cond << 28) | (rd << 12) | rd); } - - /* - * Byte swap, leaving the result at the top of the register. - * We will then shift down, zero or sign-extending. - */ - if (flags & TCG_BSWAP_IZ) { - /* - * rn = 00AB - * ror tmp, rn, #8 tmp = B00A - * orr tmp, tmp, tmp, lsl #16 tmp = BA00 - */ - tcg_out_dat_reg(s, cond, ARITH_MOV, - TCG_REG_TMP, 0, rn, SHIFT_IMM_ROR(8)); - tcg_out_dat_reg(s, cond, ARITH_ORR, - TCG_REG_TMP, TCG_REG_TMP, TCG_REG_TMP, - SHIFT_IMM_LSL(16)); - } else { - /* - * rn = xxAB - * and tmp, rn, #0xff00 tmp = 00A0 - * lsl tmp, tmp, #8 tmp = 0A00 - * orr tmp, tmp, rn, lsl #24 tmp = BA00 - */ - tcg_out_dat_rI(s, cond, ARITH_AND, TCG_REG_TMP, rn, 0xff00, 1); - tcg_out_dat_reg(s, cond, ARITH_MOV, - TCG_REG_TMP, 0, TCG_REG_TMP, SHIFT_IMM_LSL(8)); - tcg_out_dat_reg(s, cond, ARITH_ORR, - TCG_REG_TMP, TCG_REG_TMP, rn, SHIFT_IMM_LSL(24)); - } - tcg_out_dat_reg(s, cond, ARITH_MOV, rd, 0, TCG_REG_TMP, - (flags & TCG_BSWAP_OS - ? SHIFT_IMM_ASR(8) : SHIFT_IMM_LSR(8))); } static void tcg_out_bswap32(TCGContext *s, ARMCond cond, TCGReg rd, TCGReg rn) { - if (use_armv6_instructions) { - /* rev */ - tcg_out32(s, 0x06bf0f30 | (cond << 28) | (rd << 12) | rn); - } else { - tcg_out_dat_reg(s, cond, ARITH_EOR, - TCG_REG_TMP, rn, rn, SHIFT_IMM_ROR(16)); - tcg_out_dat_imm(s, cond, ARITH_BIC, - TCG_REG_TMP, TCG_REG_TMP, 0xff | 0x800); - tcg_out_dat_reg(s, cond, ARITH_MOV, - rd, 0, rn, SHIFT_IMM_ROR(8)); - tcg_out_dat_reg(s, cond, ARITH_EOR, - rd, rd, TCG_REG_TMP, SHIFT_IMM_LSR(8)); - } + /* rev */ + tcg_out32(s, 0x06bf0f30 | (cond << 28) | (rd << 12) | rn); } static void tcg_out_deposit(TCGContext *s, ARMCond cond, TCGReg rd, @@ -1283,7 +1167,7 @@ static void tcg_out_mb(TCGContext *s, TCGArg a0) { if (use_armv7_instructions) { tcg_out32(s, INSN_DMB_ISH); - } else if (use_armv6_instructions) { + } else { tcg_out32(s, INSN_DMB_MCR); } } @@ -1489,8 +1373,7 @@ static TCGReg tcg_out_arg_reg64(TCGContext *s, TCGReg argreg, if (argreg & 1) { argreg++; } - if (use_armv6_instructions && argreg >= 4 - && (arglo & 1) == 0 && arghi == arglo + 1) { + if (argreg >= 4 && (arglo & 1) == 0 && arghi == arglo + 1) { tcg_out_strd_8(s, COND_AL, arglo, TCG_REG_CALL_STACK, (argreg - 4) * 4); return argreg + 2; @@ -1520,8 +1403,6 @@ static TCGReg tcg_out_tlb_read(TCGContext *s, TCGReg addrlo, TCGReg addrhi, int cmp_off = (is_load ? offsetof(CPUTLBEntry, addr_read) : offsetof(CPUTLBEntry, addr_write)); int fast_off = TLB_MASK_TABLE_OFS(mem_index); - int mask_off = fast_off + offsetof(CPUTLBDescFast, mask); - int table_off = fast_off + offsetof(CPUTLBDescFast, table); unsigned s_bits = opc & MO_SIZE; unsigned a_bits = get_alignment_bits(opc); @@ -1534,12 +1415,7 @@ static TCGReg tcg_out_tlb_read(TCGContext *s, TCGReg addrlo, TCGReg addrhi, } /* Load env_tlb(env)->f[mmu_idx].{mask,table} into {r0,r1}. */ - if (use_armv6_instructions) { - tcg_out_ldrd_8(s, COND_AL, TCG_REG_R0, TCG_AREG0, fast_off); - } else { - tcg_out_ld(s, TCG_TYPE_I32, TCG_REG_R0, TCG_AREG0, mask_off); - tcg_out_ld(s, TCG_TYPE_I32, TCG_REG_R1, TCG_AREG0, table_off); - } + tcg_out_ldrd_8(s, COND_AL, TCG_REG_R0, TCG_AREG0, fast_off); /* Extract the tlb index from the address into R0. */ tcg_out_dat_reg(s, COND_AL, ARITH_AND, TCG_REG_R0, TCG_REG_R0, addrlo, @@ -1550,7 +1426,7 @@ static TCGReg tcg_out_tlb_read(TCGContext *s, TCGReg addrlo, TCGReg addrhi, * Load the tlb comparator into R2/R3 and the fast path addend into R1. */ if (cmp_off == 0) { - if (use_armv6_instructions && TARGET_LONG_BITS == 64) { + if (TARGET_LONG_BITS == 64) { tcg_out_ldrd_rwb(s, COND_AL, TCG_REG_R2, TCG_REG_R1, TCG_REG_R0); } else { tcg_out_ld32_rwb(s, COND_AL, TCG_REG_R2, TCG_REG_R1, TCG_REG_R0); @@ -1558,15 +1434,12 @@ static TCGReg tcg_out_tlb_read(TCGContext *s, TCGReg addrlo, TCGReg addrhi, } else { tcg_out_dat_reg(s, COND_AL, ARITH_ADD, TCG_REG_R1, TCG_REG_R1, TCG_REG_R0, 0); - if (use_armv6_instructions && TARGET_LONG_BITS == 64) { + if (TARGET_LONG_BITS == 64) { tcg_out_ldrd_8(s, COND_AL, TCG_REG_R2, TCG_REG_R1, cmp_off); } else { tcg_out_ld32_12(s, COND_AL, TCG_REG_R2, TCG_REG_R1, cmp_off); } } - if (!use_armv6_instructions && TARGET_LONG_BITS == 64) { - tcg_out_ld32_12(s, COND_AL, TCG_REG_R3, TCG_REG_R1, cmp_off + 4); - } /* Load the tlb addend. */ tcg_out_ld32_12(s, COND_AL, TCG_REG_R1, TCG_REG_R1, @@ -1631,7 +1504,6 @@ static bool tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *lb) TCGReg argreg, datalo, datahi; MemOpIdx oi = lb->oi; MemOp opc = get_memop(oi); - void *func; if (!reloc_pc24(lb->label_ptr[0], tcg_splitwx_to_rx(s->code_ptr))) { return false; @@ -1646,18 +1518,8 @@ static bool tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *lb) argreg = tcg_out_arg_imm32(s, argreg, oi); argreg = tcg_out_arg_reg32(s, argreg, TCG_REG_R14); - /* For armv6 we can use the canonical unsigned helpers and minimize - icache usage. For pre-armv6, use the signed helpers since we do - not have a single insn sign-extend. */ - if (use_armv6_instructions) { - func = qemu_ld_helpers[opc & MO_SIZE]; - } else { - func = qemu_ld_helpers[opc & MO_SSIZE]; - if (opc & MO_SIGN) { - opc = MO_UL; - } - } - tcg_out_call(s, func); + /* Use the canonical unsigned helpers and minimize icache usage. */ + tcg_out_call(s, qemu_ld_helpers[opc & MO_SIZE]); datalo = lb->datalo_reg; datahi = lb->datahi_reg; @@ -1760,7 +1622,7 @@ static void tcg_out_qemu_ld_index(TCGContext *s, MemOp opc, break; case MO_UQ: /* Avoid ldrd for user-only emulation, to handle unaligned. */ - if (USING_SOFTMMU && use_armv6_instructions + if (USING_SOFTMMU && (datalo & 1) == 0 && datahi == datalo + 1) { tcg_out_ldrd_r(s, COND_AL, datalo, addrlo, addend); } else if (datalo != addend) { @@ -1803,7 +1665,7 @@ static void tcg_out_qemu_ld_direct(TCGContext *s, MemOp opc, TCGReg datalo, break; case MO_UQ: /* Avoid ldrd for user-only emulation, to handle unaligned. */ - if (USING_SOFTMMU && use_armv6_instructions + if (USING_SOFTMMU && (datalo & 1) == 0 && datahi == datalo + 1) { tcg_out_ldrd_8(s, COND_AL, datalo, addrlo, 0); } else if (datalo == addrlo) { @@ -1880,7 +1742,7 @@ static void tcg_out_qemu_st_index(TCGContext *s, ARMCond cond, MemOp opc, break; case MO_64: /* Avoid strd for user-only emulation, to handle unaligned. */ - if (USING_SOFTMMU && use_armv6_instructions + if (USING_SOFTMMU && (datalo & 1) == 0 && datahi == datalo + 1) { tcg_out_strd_r(s, cond, datalo, addrlo, addend); } else { @@ -1912,7 +1774,7 @@ static void tcg_out_qemu_st_direct(TCGContext *s, MemOp opc, TCGReg datalo, break; case MO_64: /* Avoid strd for user-only emulation, to handle unaligned. */ - if (USING_SOFTMMU && use_armv6_instructions + if (USING_SOFTMMU && (datalo & 1) == 0 && datahi == datalo + 1) { tcg_out_strd_8(s, COND_AL, datalo, addrlo, 0); } else { diff --git a/tcg/arm/tcg-target.h b/tcg/arm/tcg-target.h index 5c9ba5feea..1dd4cd5377 100644 --- a/tcg/arm/tcg-target.h +++ b/tcg/arm/tcg-target.h @@ -28,7 +28,6 @@ extern int arm_arch; -#define use_armv6_instructions (__ARM_ARCH >= 6 || arm_arch >= 6) #define use_armv7_instructions (__ARM_ARCH >= 7 || arm_arch >= 7) #undef TCG_TARGET_STACK_GROWSUP From 367d43d85b8a0f6262125ccbad8720d02416265e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 2 Jan 2022 21:26:17 -0800 Subject: [PATCH 314/460] tcg/arm: Check alignment for ldrd and strd We will shortly allow the use of unaligned memory accesses, and these require proper alignment. Use get_alignment_bits to verify and remove USING_SOFTMMU. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- tcg/arm/tcg-target.c.inc | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc index 9eb43407ea..4b0b4f4c2f 100644 --- a/tcg/arm/tcg-target.c.inc +++ b/tcg/arm/tcg-target.c.inc @@ -34,13 +34,6 @@ bool use_idiv_instructions; bool use_neon_instructions; #endif -/* ??? Ought to think about changing CONFIG_SOFTMMU to always defined. */ -#ifdef CONFIG_SOFTMMU -# define USING_SOFTMMU 1 -#else -# define USING_SOFTMMU 0 -#endif - #ifdef CONFIG_DEBUG_TCG static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = { "%r0", "%r1", "%r2", "%r3", "%r4", "%r5", "%r6", "%r7", @@ -1621,8 +1614,8 @@ static void tcg_out_qemu_ld_index(TCGContext *s, MemOp opc, tcg_out_ld32_r(s, COND_AL, datalo, addrlo, addend); break; case MO_UQ: - /* Avoid ldrd for user-only emulation, to handle unaligned. */ - if (USING_SOFTMMU + /* LDRD requires alignment; double-check that. */ + if (get_alignment_bits(opc) >= MO_64 && (datalo & 1) == 0 && datahi == datalo + 1) { tcg_out_ldrd_r(s, COND_AL, datalo, addrlo, addend); } else if (datalo != addend) { @@ -1664,8 +1657,8 @@ static void tcg_out_qemu_ld_direct(TCGContext *s, MemOp opc, TCGReg datalo, tcg_out_ld32_12(s, COND_AL, datalo, addrlo, 0); break; case MO_UQ: - /* Avoid ldrd for user-only emulation, to handle unaligned. */ - if (USING_SOFTMMU + /* LDRD requires alignment; double-check that. */ + if (get_alignment_bits(opc) >= MO_64 && (datalo & 1) == 0 && datahi == datalo + 1) { tcg_out_ldrd_8(s, COND_AL, datalo, addrlo, 0); } else if (datalo == addrlo) { @@ -1741,8 +1734,8 @@ static void tcg_out_qemu_st_index(TCGContext *s, ARMCond cond, MemOp opc, tcg_out_st32_r(s, cond, datalo, addrlo, addend); break; case MO_64: - /* Avoid strd for user-only emulation, to handle unaligned. */ - if (USING_SOFTMMU + /* STRD requires alignment; double-check that. */ + if (get_alignment_bits(opc) >= MO_64 && (datalo & 1) == 0 && datahi == datalo + 1) { tcg_out_strd_r(s, cond, datalo, addrlo, addend); } else { @@ -1773,8 +1766,8 @@ static void tcg_out_qemu_st_direct(TCGContext *s, MemOp opc, TCGReg datalo, tcg_out_st32_12(s, COND_AL, datalo, addrlo, 0); break; case MO_64: - /* Avoid strd for user-only emulation, to handle unaligned. */ - if (USING_SOFTMMU + /* STRD requires alignment; double-check that. */ + if (get_alignment_bits(opc) >= MO_64 && (datalo & 1) == 0 && datahi == datalo + 1) { tcg_out_strd_8(s, COND_AL, datalo, addrlo, 0); } else { From 8821ec2323dd8793d840fd455c5e20e144bddc9b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 7 Aug 2021 18:19:14 -1000 Subject: [PATCH 315/460] tcg/arm: Support unaligned access for softmmu From armv6, the architecture supports unaligned accesses. All we need to do is perform the correct alignment check in tcg_out_tlb_read. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- tcg/arm/tcg-target.c.inc | 41 ++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc index 4b0b4f4c2f..d290b4556c 100644 --- a/tcg/arm/tcg-target.c.inc +++ b/tcg/arm/tcg-target.c.inc @@ -1396,16 +1396,9 @@ static TCGReg tcg_out_tlb_read(TCGContext *s, TCGReg addrlo, TCGReg addrhi, int cmp_off = (is_load ? offsetof(CPUTLBEntry, addr_read) : offsetof(CPUTLBEntry, addr_write)); int fast_off = TLB_MASK_TABLE_OFS(mem_index); - unsigned s_bits = opc & MO_SIZE; - unsigned a_bits = get_alignment_bits(opc); - - /* - * We don't support inline unaligned acceses, but we can easily - * support overalignment checks. - */ - if (a_bits < s_bits) { - a_bits = s_bits; - } + unsigned s_mask = (1 << (opc & MO_SIZE)) - 1; + unsigned a_mask = (1 << get_alignment_bits(opc)) - 1; + TCGReg t_addr; /* Load env_tlb(env)->f[mmu_idx].{mask,table} into {r0,r1}. */ tcg_out_ldrd_8(s, COND_AL, TCG_REG_R0, TCG_AREG0, fast_off); @@ -1440,27 +1433,35 @@ static TCGReg tcg_out_tlb_read(TCGContext *s, TCGReg addrlo, TCGReg addrhi, /* * Check alignment, check comparators. - * Do this in no more than 3 insns. Use MOVW for v7, if possible, + * Do this in 2-4 insns. Use MOVW for v7, if possible, * to reduce the number of sequential conditional instructions. * Almost all guests have at least 4k pages, which means that we need * to clear at least 9 bits even for an 8-byte memory, which means it * isn't worth checking for an immediate operand for BIC. + * + * For unaligned accesses, test the page of the last unit of alignment. + * This leaves the least significant alignment bits unchanged, and of + * course must be zero. */ + t_addr = addrlo; + if (a_mask < s_mask) { + t_addr = TCG_REG_R0; + tcg_out_dat_imm(s, COND_AL, ARITH_ADD, t_addr, + addrlo, s_mask - a_mask); + } if (use_armv7_instructions && TARGET_PAGE_BITS <= 16) { - tcg_target_ulong mask = ~(TARGET_PAGE_MASK | ((1 << a_bits) - 1)); - - tcg_out_movi32(s, COND_AL, TCG_REG_TMP, mask); + tcg_out_movi32(s, COND_AL, TCG_REG_TMP, ~(TARGET_PAGE_MASK | a_mask)); tcg_out_dat_reg(s, COND_AL, ARITH_BIC, TCG_REG_TMP, - addrlo, TCG_REG_TMP, 0); + t_addr, TCG_REG_TMP, 0); tcg_out_dat_reg(s, COND_AL, ARITH_CMP, 0, TCG_REG_R2, TCG_REG_TMP, 0); } else { - if (a_bits) { - tcg_out_dat_imm(s, COND_AL, ARITH_TST, 0, addrlo, - (1 << a_bits) - 1); + if (a_mask) { + tcg_debug_assert(a_mask <= 0xff); + tcg_out_dat_imm(s, COND_AL, ARITH_TST, 0, addrlo, a_mask); } - tcg_out_dat_reg(s, COND_AL, ARITH_MOV, TCG_REG_TMP, 0, addrlo, + tcg_out_dat_reg(s, COND_AL, ARITH_MOV, TCG_REG_TMP, 0, t_addr, SHIFT_IMM_LSR(TARGET_PAGE_BITS)); - tcg_out_dat_reg(s, (a_bits ? COND_EQ : COND_AL), ARITH_CMP, + tcg_out_dat_reg(s, (a_mask ? COND_EQ : COND_AL), ARITH_CMP, 0, TCG_REG_R2, TCG_REG_TMP, SHIFT_IMM_LSL(TARGET_PAGE_BITS)); } From 4bb802073f212b5a7e7cffcd4456484932911b91 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 9 Aug 2021 17:44:50 -1000 Subject: [PATCH 316/460] tcg/arm: Reserve a register for guest_base Reserve a register for the guest_base using aarch64 for reference. By doing so, we do not have to recompute it for every memory load. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- tcg/arm/tcg-target.c.inc | 39 ++++++++++++++++++++++++++++----------- 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc index d290b4556c..7eebbfaf02 100644 --- a/tcg/arm/tcg-target.c.inc +++ b/tcg/arm/tcg-target.c.inc @@ -84,6 +84,9 @@ static const int tcg_target_call_oarg_regs[2] = { #define TCG_REG_TMP TCG_REG_R12 #define TCG_VEC_TMP TCG_REG_Q15 +#ifndef CONFIG_SOFTMMU +#define TCG_REG_GUEST_BASE TCG_REG_R11 +#endif typedef enum { COND_EQ = 0x0, @@ -1593,7 +1596,8 @@ static bool tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *lb) static void tcg_out_qemu_ld_index(TCGContext *s, MemOp opc, TCGReg datalo, TCGReg datahi, - TCGReg addrlo, TCGReg addend) + TCGReg addrlo, TCGReg addend, + bool scratch_addend) { /* Byte swapping is left to middle-end expansion. */ tcg_debug_assert((opc & MO_BSWAP) == 0); @@ -1619,7 +1623,7 @@ static void tcg_out_qemu_ld_index(TCGContext *s, MemOp opc, if (get_alignment_bits(opc) >= MO_64 && (datalo & 1) == 0 && datahi == datalo + 1) { tcg_out_ldrd_r(s, COND_AL, datalo, addrlo, addend); - } else if (datalo != addend) { + } else if (scratch_addend) { tcg_out_ld32_rwb(s, COND_AL, datalo, addend, addrlo); tcg_out_ld32_12(s, COND_AL, datahi, addend, 4); } else { @@ -1703,14 +1707,14 @@ static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, bool is64) label_ptr = s->code_ptr; tcg_out_bl_imm(s, COND_NE, 0); - tcg_out_qemu_ld_index(s, opc, datalo, datahi, addrlo, addend); + tcg_out_qemu_ld_index(s, opc, datalo, datahi, addrlo, addend, true); add_qemu_ldst_label(s, true, oi, datalo, datahi, addrlo, addrhi, s->code_ptr, label_ptr); #else /* !CONFIG_SOFTMMU */ if (guest_base) { - tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_TMP, guest_base); - tcg_out_qemu_ld_index(s, opc, datalo, datahi, addrlo, TCG_REG_TMP); + tcg_out_qemu_ld_index(s, opc, datalo, datahi, + addrlo, TCG_REG_GUEST_BASE, false); } else { tcg_out_qemu_ld_direct(s, opc, datalo, datahi, addrlo); } @@ -1719,7 +1723,8 @@ static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, bool is64) static void tcg_out_qemu_st_index(TCGContext *s, ARMCond cond, MemOp opc, TCGReg datalo, TCGReg datahi, - TCGReg addrlo, TCGReg addend) + TCGReg addrlo, TCGReg addend, + bool scratch_addend) { /* Byte swapping is left to middle-end expansion. */ tcg_debug_assert((opc & MO_BSWAP) == 0); @@ -1739,9 +1744,14 @@ static void tcg_out_qemu_st_index(TCGContext *s, ARMCond cond, MemOp opc, if (get_alignment_bits(opc) >= MO_64 && (datalo & 1) == 0 && datahi == datalo + 1) { tcg_out_strd_r(s, cond, datalo, addrlo, addend); - } else { + } else if (scratch_addend) { tcg_out_st32_rwb(s, cond, datalo, addend, addrlo); tcg_out_st32_12(s, cond, datahi, addend, 4); + } else { + tcg_out_dat_reg(s, cond, ARITH_ADD, TCG_REG_TMP, + addend, addrlo, SHIFT_IMM_LSL(0)); + tcg_out_st32_12(s, cond, datalo, TCG_REG_TMP, 0); + tcg_out_st32_12(s, cond, datahi, TCG_REG_TMP, 4); } break; default: @@ -1804,7 +1814,8 @@ static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args, bool is64) mem_index = get_mmuidx(oi); addend = tcg_out_tlb_read(s, addrlo, addrhi, opc, mem_index, 0); - tcg_out_qemu_st_index(s, COND_EQ, opc, datalo, datahi, addrlo, addend); + tcg_out_qemu_st_index(s, COND_EQ, opc, datalo, datahi, + addrlo, addend, true); /* The conditional call must come last, as we're going to return here. */ label_ptr = s->code_ptr; @@ -1814,9 +1825,8 @@ static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args, bool is64) s->code_ptr, label_ptr); #else /* !CONFIG_SOFTMMU */ if (guest_base) { - tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_TMP, guest_base); - tcg_out_qemu_st_index(s, COND_AL, opc, datalo, - datahi, addrlo, TCG_REG_TMP); + tcg_out_qemu_st_index(s, COND_AL, opc, datalo, datahi, + addrlo, TCG_REG_GUEST_BASE, false); } else { tcg_out_qemu_st_direct(s, opc, datalo, datahi, addrlo); } @@ -2958,6 +2968,13 @@ static void tcg_target_qemu_prologue(TCGContext *s) tcg_out_mov(s, TCG_TYPE_PTR, TCG_AREG0, tcg_target_call_iarg_regs[0]); +#ifndef CONFIG_SOFTMMU + if (guest_base) { + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_GUEST_BASE, guest_base); + tcg_regset_set_reg(s->reserved_regs, TCG_REG_GUEST_BASE); + } +#endif + tcg_out_b_reg(s, COND_AL, tcg_target_call_iarg_regs[1]); /* From 0c90fa5dce29243c06841d7b07ff2bd97c27c1f4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 9 Aug 2021 19:18:27 -1000 Subject: [PATCH 317/460] tcg/arm: Support raising sigbus for user-only Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- tcg/arm/tcg-target.c.inc | 83 +++++++++++++++++++++++++++++++++++++++- tcg/arm/tcg-target.h | 2 - 2 files changed, 81 insertions(+), 4 deletions(-) diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc index 7eebbfaf02..e1ea69669c 100644 --- a/tcg/arm/tcg-target.c.inc +++ b/tcg/arm/tcg-target.c.inc @@ -23,6 +23,7 @@ */ #include "elf.h" +#include "../tcg-ldst.c.inc" #include "../tcg-pool.c.inc" int arm_arch = __ARM_ARCH; @@ -1289,8 +1290,6 @@ static void tcg_out_vldst(TCGContext *s, ARMInsn insn, } #ifdef CONFIG_SOFTMMU -#include "../tcg-ldst.c.inc" - /* helper signature: helper_ret_ld_mmu(CPUState *env, target_ulong addr, * int mmu_idx, uintptr_t ra) */ @@ -1592,6 +1591,74 @@ static bool tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *lb) tcg_out_goto(s, COND_AL, qemu_st_helpers[opc & MO_SIZE]); return true; } +#else + +static void tcg_out_test_alignment(TCGContext *s, bool is_ld, TCGReg addrlo, + TCGReg addrhi, unsigned a_bits) +{ + unsigned a_mask = (1 << a_bits) - 1; + TCGLabelQemuLdst *label = new_ldst_label(s); + + label->is_ld = is_ld; + label->addrlo_reg = addrlo; + label->addrhi_reg = addrhi; + + /* We are expecting a_bits to max out at 7, and can easily support 8. */ + tcg_debug_assert(a_mask <= 0xff); + /* tst addr, #mask */ + tcg_out_dat_imm(s, COND_AL, ARITH_TST, 0, addrlo, a_mask); + + /* blne slow_path */ + label->label_ptr[0] = s->code_ptr; + tcg_out_bl_imm(s, COND_NE, 0); + + label->raddr = tcg_splitwx_to_rx(s->code_ptr); +} + +static bool tcg_out_fail_alignment(TCGContext *s, TCGLabelQemuLdst *l) +{ + if (!reloc_pc24(l->label_ptr[0], tcg_splitwx_to_rx(s->code_ptr))) { + return false; + } + + if (TARGET_LONG_BITS == 64) { + /* 64-bit target address is aligned into R2:R3. */ + if (l->addrhi_reg != TCG_REG_R2) { + tcg_out_mov(s, TCG_TYPE_I32, TCG_REG_R2, l->addrlo_reg); + tcg_out_mov(s, TCG_TYPE_I32, TCG_REG_R3, l->addrhi_reg); + } else if (l->addrlo_reg != TCG_REG_R3) { + tcg_out_mov(s, TCG_TYPE_I32, TCG_REG_R3, l->addrhi_reg); + tcg_out_mov(s, TCG_TYPE_I32, TCG_REG_R2, l->addrlo_reg); + } else { + tcg_out_mov(s, TCG_TYPE_I32, TCG_REG_R1, TCG_REG_R2); + tcg_out_mov(s, TCG_TYPE_I32, TCG_REG_R2, TCG_REG_R3); + tcg_out_mov(s, TCG_TYPE_I32, TCG_REG_R3, TCG_REG_R1); + } + } else { + tcg_out_mov(s, TCG_TYPE_I32, TCG_REG_R1, l->addrlo_reg); + } + tcg_out_mov(s, TCG_TYPE_PTR, TCG_REG_R0, TCG_AREG0); + + /* + * Tail call to the helper, with the return address back inline, + * just for the clarity of the debugging traceback -- the helper + * cannot return. We have used BLNE to arrive here, so LR is + * already set. + */ + tcg_out_goto(s, COND_AL, (const void *) + (l->is_ld ? helper_unaligned_ld : helper_unaligned_st)); + return true; +} + +static bool tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *l) +{ + return tcg_out_fail_alignment(s, l); +} + +static bool tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *l) +{ + return tcg_out_fail_alignment(s, l); +} #endif /* SOFTMMU */ static void tcg_out_qemu_ld_index(TCGContext *s, MemOp opc, @@ -1689,6 +1756,8 @@ static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, bool is64) int mem_index; TCGReg addend; tcg_insn_unit *label_ptr; +#else + unsigned a_bits; #endif datalo = *args++; @@ -1712,6 +1781,10 @@ static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, bool is64) add_qemu_ldst_label(s, true, oi, datalo, datahi, addrlo, addrhi, s->code_ptr, label_ptr); #else /* !CONFIG_SOFTMMU */ + a_bits = get_alignment_bits(opc); + if (a_bits) { + tcg_out_test_alignment(s, true, addrlo, addrhi, a_bits); + } if (guest_base) { tcg_out_qemu_ld_index(s, opc, datalo, datahi, addrlo, TCG_REG_GUEST_BASE, false); @@ -1801,6 +1874,8 @@ static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args, bool is64) int mem_index; TCGReg addend; tcg_insn_unit *label_ptr; +#else + unsigned a_bits; #endif datalo = *args++; @@ -1824,6 +1899,10 @@ static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args, bool is64) add_qemu_ldst_label(s, false, oi, datalo, datahi, addrlo, addrhi, s->code_ptr, label_ptr); #else /* !CONFIG_SOFTMMU */ + a_bits = get_alignment_bits(opc); + if (a_bits) { + tcg_out_test_alignment(s, false, addrlo, addrhi, a_bits); + } if (guest_base) { tcg_out_qemu_st_index(s, COND_AL, opc, datalo, datahi, addrlo, TCG_REG_GUEST_BASE, false); diff --git a/tcg/arm/tcg-target.h b/tcg/arm/tcg-target.h index 1dd4cd5377..27c27a1f14 100644 --- a/tcg/arm/tcg-target.h +++ b/tcg/arm/tcg-target.h @@ -151,9 +151,7 @@ extern bool use_neon_instructions; /* not defined -- call should be eliminated at compile time */ void tb_target_set_jmp_target(uintptr_t, uintptr_t, uintptr_t, uintptr_t); -#ifdef CONFIG_SOFTMMU #define TCG_TARGET_NEED_LDST_LABELS -#endif #define TCG_TARGET_NEED_POOL_LABELS #endif From 23a79c113ed2ae693d882d109862f4a759fbf10e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 6 Aug 2021 05:49:16 -1000 Subject: [PATCH 318/460] tcg/mips: Support unaligned access for user-only MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is kinda sorta the opposite of the other tcg hosts, where we get (normal) alignment checks for free with host SIGBUS and need to add code to support unaligned accesses. Fortunately, the ISA contains pairs of instructions that are used to implement unaligned memory accesses. Use them. Tested-by: Jiaxun Yang Reviewed-by: Jiaxun Yang Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/mips/tcg-target.c.inc | 334 +++++++++++++++++++++++++++++++++++++- tcg/mips/tcg-target.h | 2 - 2 files changed, 328 insertions(+), 8 deletions(-) diff --git a/tcg/mips/tcg-target.c.inc b/tcg/mips/tcg-target.c.inc index 27b020e66c..2c94ac2ed6 100644 --- a/tcg/mips/tcg-target.c.inc +++ b/tcg/mips/tcg-target.c.inc @@ -24,6 +24,8 @@ * THE SOFTWARE. */ +#include "../tcg-ldst.c.inc" + #ifdef HOST_WORDS_BIGENDIAN # define MIPS_BE 1 #else @@ -230,16 +232,26 @@ typedef enum { OPC_ORI = 015 << 26, OPC_XORI = 016 << 26, OPC_LUI = 017 << 26, + OPC_BNEL = 025 << 26, + OPC_BNEZALC_R6 = 030 << 26, OPC_DADDIU = 031 << 26, + OPC_LDL = 032 << 26, + OPC_LDR = 033 << 26, OPC_LB = 040 << 26, OPC_LH = 041 << 26, + OPC_LWL = 042 << 26, OPC_LW = 043 << 26, OPC_LBU = 044 << 26, OPC_LHU = 045 << 26, + OPC_LWR = 046 << 26, OPC_LWU = 047 << 26, OPC_SB = 050 << 26, OPC_SH = 051 << 26, + OPC_SWL = 052 << 26, OPC_SW = 053 << 26, + OPC_SDL = 054 << 26, + OPC_SDR = 055 << 26, + OPC_SWR = 056 << 26, OPC_LD = 067 << 26, OPC_SD = 077 << 26, @@ -1015,8 +1027,6 @@ static void tcg_out_call(TCGContext *s, const tcg_insn_unit *arg) } #if defined(CONFIG_SOFTMMU) -#include "../tcg-ldst.c.inc" - static void * const qemu_ld_helpers[(MO_SSIZE | MO_BSWAP) + 1] = { [MO_UB] = helper_ret_ldub_mmu, [MO_SB] = helper_ret_ldsb_mmu, @@ -1324,7 +1334,82 @@ static bool tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *l) tcg_out_mov(s, TCG_TYPE_PTR, tcg_target_call_iarg_regs[0], TCG_AREG0); return true; } -#endif + +#else + +static void tcg_out_test_alignment(TCGContext *s, bool is_ld, TCGReg addrlo, + TCGReg addrhi, unsigned a_bits) +{ + unsigned a_mask = (1 << a_bits) - 1; + TCGLabelQemuLdst *l = new_ldst_label(s); + + l->is_ld = is_ld; + l->addrlo_reg = addrlo; + l->addrhi_reg = addrhi; + + /* We are expecting a_bits to max out at 7, much lower than ANDI. */ + tcg_debug_assert(a_bits < 16); + tcg_out_opc_imm(s, OPC_ANDI, TCG_TMP0, addrlo, a_mask); + + l->label_ptr[0] = s->code_ptr; + if (use_mips32r6_instructions) { + tcg_out_opc_br(s, OPC_BNEZALC_R6, TCG_REG_ZERO, TCG_TMP0); + } else { + tcg_out_opc_br(s, OPC_BNEL, TCG_TMP0, TCG_REG_ZERO); + tcg_out_nop(s); + } + + l->raddr = tcg_splitwx_to_rx(s->code_ptr); +} + +static bool tcg_out_fail_alignment(TCGContext *s, TCGLabelQemuLdst *l) +{ + void *target; + + if (!reloc_pc16(l->label_ptr[0], tcg_splitwx_to_rx(s->code_ptr))) { + return false; + } + + if (TCG_TARGET_REG_BITS < TARGET_LONG_BITS) { + /* A0 is env, A1 is skipped, A2:A3 is the uint64_t address. */ + TCGReg a2 = MIPS_BE ? l->addrhi_reg : l->addrlo_reg; + TCGReg a3 = MIPS_BE ? l->addrlo_reg : l->addrhi_reg; + + if (a3 != TCG_REG_A2) { + tcg_out_mov(s, TCG_TYPE_I32, TCG_REG_A2, a2); + tcg_out_mov(s, TCG_TYPE_I32, TCG_REG_A3, a3); + } else if (a2 != TCG_REG_A3) { + tcg_out_mov(s, TCG_TYPE_I32, TCG_REG_A3, a3); + tcg_out_mov(s, TCG_TYPE_I32, TCG_REG_A2, a2); + } else { + tcg_out_mov(s, TCG_TYPE_I32, TCG_TMP0, TCG_REG_A2); + tcg_out_mov(s, TCG_TYPE_I32, TCG_REG_A2, TCG_REG_A3); + tcg_out_mov(s, TCG_TYPE_I32, TCG_REG_A3, TCG_TMP0); + } + } else { + tcg_out_mov(s, TCG_TYPE_TL, TCG_REG_A1, l->addrlo_reg); + } + tcg_out_mov(s, TCG_TYPE_PTR, TCG_REG_A0, TCG_AREG0); + + /* + * Tail call to the helper, with the return address back inline. + * We have arrived here via BNEL, so $31 is already set. + */ + target = (l->is_ld ? helper_unaligned_ld : helper_unaligned_st); + tcg_out_call_int(s, target, true); + return true; +} + +static bool tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *l) +{ + return tcg_out_fail_alignment(s, l); +} + +static bool tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *l) +{ + return tcg_out_fail_alignment(s, l); +} +#endif /* SOFTMMU */ static void tcg_out_qemu_ld_direct(TCGContext *s, TCGReg lo, TCGReg hi, TCGReg base, MemOp opc, bool is_64) @@ -1430,6 +1515,127 @@ static void tcg_out_qemu_ld_direct(TCGContext *s, TCGReg lo, TCGReg hi, } } +static void __attribute__((unused)) +tcg_out_qemu_ld_unalign(TCGContext *s, TCGReg lo, TCGReg hi, + TCGReg base, MemOp opc, bool is_64) +{ + const MIPSInsn lw1 = MIPS_BE ? OPC_LWL : OPC_LWR; + const MIPSInsn lw2 = MIPS_BE ? OPC_LWR : OPC_LWL; + const MIPSInsn ld1 = MIPS_BE ? OPC_LDL : OPC_LDR; + const MIPSInsn ld2 = MIPS_BE ? OPC_LDR : OPC_LDL; + + bool sgn = (opc & MO_SIGN); + + switch (opc & (MO_SSIZE | MO_BSWAP)) { + case MO_SW | MO_BE: + case MO_UW | MO_BE: + tcg_out_opc_imm(s, sgn ? OPC_LB : OPC_LBU, TCG_TMP0, base, 0); + tcg_out_opc_imm(s, OPC_LBU, lo, base, 1); + if (use_mips32r2_instructions) { + tcg_out_opc_bf(s, OPC_INS, lo, TCG_TMP0, 31, 8); + } else { + tcg_out_opc_sa(s, OPC_SLL, TCG_TMP0, TCG_TMP0, 8); + tcg_out_opc_reg(s, OPC_OR, lo, TCG_TMP0, TCG_TMP1); + } + break; + + case MO_SW | MO_LE: + case MO_UW | MO_LE: + if (use_mips32r2_instructions && lo != base) { + tcg_out_opc_imm(s, OPC_LBU, lo, base, 0); + tcg_out_opc_imm(s, sgn ? OPC_LB : OPC_LBU, TCG_TMP0, base, 1); + tcg_out_opc_bf(s, OPC_INS, lo, TCG_TMP0, 31, 8); + } else { + tcg_out_opc_imm(s, OPC_LBU, TCG_TMP0, base, 0); + tcg_out_opc_imm(s, sgn ? OPC_LB : OPC_LBU, TCG_TMP1, base, 1); + tcg_out_opc_sa(s, OPC_SLL, TCG_TMP1, TCG_TMP1, 8); + tcg_out_opc_reg(s, OPC_OR, lo, TCG_TMP0, TCG_TMP1); + } + break; + + case MO_SL: + case MO_UL: + tcg_out_opc_imm(s, lw1, lo, base, 0); + tcg_out_opc_imm(s, lw2, lo, base, 3); + if (TCG_TARGET_REG_BITS == 64 && is_64 && !sgn) { + tcg_out_ext32u(s, lo, lo); + } + break; + + case MO_UL | MO_BSWAP: + case MO_SL | MO_BSWAP: + if (use_mips32r2_instructions) { + tcg_out_opc_imm(s, lw1, lo, base, 0); + tcg_out_opc_imm(s, lw2, lo, base, 3); + tcg_out_bswap32(s, lo, lo, + TCG_TARGET_REG_BITS == 64 && is_64 + ? (sgn ? TCG_BSWAP_OS : TCG_BSWAP_OZ) : 0); + } else { + const tcg_insn_unit *subr = + (TCG_TARGET_REG_BITS == 64 && is_64 && !sgn + ? bswap32u_addr : bswap32_addr); + + tcg_out_opc_imm(s, lw1, TCG_TMP0, base, 0); + tcg_out_bswap_subr(s, subr); + /* delay slot */ + tcg_out_opc_imm(s, lw2, TCG_TMP0, base, 3); + tcg_out_mov(s, is_64 ? TCG_TYPE_I64 : TCG_TYPE_I32, lo, TCG_TMP3); + } + break; + + case MO_UQ: + if (TCG_TARGET_REG_BITS == 64) { + tcg_out_opc_imm(s, ld1, lo, base, 0); + tcg_out_opc_imm(s, ld2, lo, base, 7); + } else { + tcg_out_opc_imm(s, lw1, MIPS_BE ? hi : lo, base, 0 + 0); + tcg_out_opc_imm(s, lw2, MIPS_BE ? hi : lo, base, 0 + 3); + tcg_out_opc_imm(s, lw1, MIPS_BE ? lo : hi, base, 4 + 0); + tcg_out_opc_imm(s, lw2, MIPS_BE ? lo : hi, base, 4 + 3); + } + break; + + case MO_UQ | MO_BSWAP: + if (TCG_TARGET_REG_BITS == 64) { + if (use_mips32r2_instructions) { + tcg_out_opc_imm(s, ld1, lo, base, 0); + tcg_out_opc_imm(s, ld2, lo, base, 7); + tcg_out_bswap64(s, lo, lo); + } else { + tcg_out_opc_imm(s, ld1, TCG_TMP0, base, 0); + tcg_out_bswap_subr(s, bswap64_addr); + /* delay slot */ + tcg_out_opc_imm(s, ld2, TCG_TMP0, base, 7); + tcg_out_mov(s, TCG_TYPE_I64, lo, TCG_TMP3); + } + } else if (use_mips32r2_instructions) { + tcg_out_opc_imm(s, lw1, TCG_TMP0, base, 0 + 0); + tcg_out_opc_imm(s, lw2, TCG_TMP0, base, 0 + 3); + tcg_out_opc_imm(s, lw1, TCG_TMP1, base, 4 + 0); + tcg_out_opc_imm(s, lw2, TCG_TMP1, base, 4 + 3); + tcg_out_opc_reg(s, OPC_WSBH, TCG_TMP0, 0, TCG_TMP0); + tcg_out_opc_reg(s, OPC_WSBH, TCG_TMP1, 0, TCG_TMP1); + tcg_out_opc_sa(s, OPC_ROTR, MIPS_BE ? lo : hi, TCG_TMP0, 16); + tcg_out_opc_sa(s, OPC_ROTR, MIPS_BE ? hi : lo, TCG_TMP1, 16); + } else { + tcg_out_opc_imm(s, lw1, TCG_TMP0, base, 0 + 0); + tcg_out_bswap_subr(s, bswap32_addr); + /* delay slot */ + tcg_out_opc_imm(s, lw2, TCG_TMP0, base, 0 + 3); + tcg_out_opc_imm(s, lw1, TCG_TMP0, base, 4 + 0); + tcg_out_mov(s, TCG_TYPE_I32, MIPS_BE ? lo : hi, TCG_TMP3); + tcg_out_bswap_subr(s, bswap32_addr); + /* delay slot */ + tcg_out_opc_imm(s, lw2, TCG_TMP0, base, 4 + 3); + tcg_out_mov(s, TCG_TYPE_I32, MIPS_BE ? hi : lo, TCG_TMP3); + } + break; + + default: + g_assert_not_reached(); + } +} + static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, bool is_64) { TCGReg addr_regl, addr_regh __attribute__((unused)); @@ -1438,6 +1644,8 @@ static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, bool is_64) MemOp opc; #if defined(CONFIG_SOFTMMU) tcg_insn_unit *label_ptr[2]; +#else + unsigned a_bits, s_bits; #endif TCGReg base = TCG_REG_A0; @@ -1467,7 +1675,27 @@ static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, bool is_64) } else { tcg_out_opc_reg(s, ALIAS_PADD, base, TCG_GUEST_BASE_REG, addr_regl); } - tcg_out_qemu_ld_direct(s, data_regl, data_regh, base, opc, is_64); + a_bits = get_alignment_bits(opc); + s_bits = opc & MO_SIZE; + /* + * R6 removes the left/right instructions but requires the + * system to support misaligned memory accesses. + */ + if (use_mips32r6_instructions) { + if (a_bits) { + tcg_out_test_alignment(s, true, addr_regl, addr_regh, a_bits); + } + tcg_out_qemu_ld_direct(s, data_regl, data_regh, base, opc, is_64); + } else { + if (a_bits && a_bits != s_bits) { + tcg_out_test_alignment(s, true, addr_regl, addr_regh, a_bits); + } + if (a_bits >= s_bits) { + tcg_out_qemu_ld_direct(s, data_regl, data_regh, base, opc, is_64); + } else { + tcg_out_qemu_ld_unalign(s, data_regl, data_regh, base, opc, is_64); + } + } #endif } @@ -1532,6 +1760,79 @@ static void tcg_out_qemu_st_direct(TCGContext *s, TCGReg lo, TCGReg hi, } } +static void __attribute__((unused)) +tcg_out_qemu_st_unalign(TCGContext *s, TCGReg lo, TCGReg hi, + TCGReg base, MemOp opc) +{ + const MIPSInsn sw1 = MIPS_BE ? OPC_SWL : OPC_SWR; + const MIPSInsn sw2 = MIPS_BE ? OPC_SWR : OPC_SWL; + const MIPSInsn sd1 = MIPS_BE ? OPC_SDL : OPC_SDR; + const MIPSInsn sd2 = MIPS_BE ? OPC_SDR : OPC_SDL; + + /* Don't clutter the code below with checks to avoid bswapping ZERO. */ + if ((lo | hi) == 0) { + opc &= ~MO_BSWAP; + } + + switch (opc & (MO_SIZE | MO_BSWAP)) { + case MO_16 | MO_BE: + tcg_out_opc_sa(s, OPC_SRL, TCG_TMP0, lo, 8); + tcg_out_opc_imm(s, OPC_SB, TCG_TMP0, base, 0); + tcg_out_opc_imm(s, OPC_SB, lo, base, 1); + break; + + case MO_16 | MO_LE: + tcg_out_opc_sa(s, OPC_SRL, TCG_TMP0, lo, 8); + tcg_out_opc_imm(s, OPC_SB, lo, base, 0); + tcg_out_opc_imm(s, OPC_SB, TCG_TMP0, base, 1); + break; + + case MO_32 | MO_BSWAP: + tcg_out_bswap32(s, TCG_TMP3, lo, 0); + lo = TCG_TMP3; + /* fall through */ + case MO_32: + tcg_out_opc_imm(s, sw1, lo, base, 0); + tcg_out_opc_imm(s, sw2, lo, base, 3); + break; + + case MO_64 | MO_BSWAP: + if (TCG_TARGET_REG_BITS == 64) { + tcg_out_bswap64(s, TCG_TMP3, lo); + lo = TCG_TMP3; + } else if (use_mips32r2_instructions) { + tcg_out_opc_reg(s, OPC_WSBH, TCG_TMP0, 0, MIPS_BE ? hi : lo); + tcg_out_opc_reg(s, OPC_WSBH, TCG_TMP1, 0, MIPS_BE ? lo : hi); + tcg_out_opc_sa(s, OPC_ROTR, TCG_TMP0, TCG_TMP0, 16); + tcg_out_opc_sa(s, OPC_ROTR, TCG_TMP1, TCG_TMP1, 16); + hi = MIPS_BE ? TCG_TMP0 : TCG_TMP1; + lo = MIPS_BE ? TCG_TMP1 : TCG_TMP0; + } else { + tcg_out_bswap32(s, TCG_TMP3, MIPS_BE ? lo : hi, 0); + tcg_out_opc_imm(s, sw1, TCG_TMP3, base, 0 + 0); + tcg_out_opc_imm(s, sw2, TCG_TMP3, base, 0 + 3); + tcg_out_bswap32(s, TCG_TMP3, MIPS_BE ? hi : lo, 0); + tcg_out_opc_imm(s, sw1, TCG_TMP3, base, 4 + 0); + tcg_out_opc_imm(s, sw2, TCG_TMP3, base, 4 + 3); + break; + } + /* fall through */ + case MO_64: + if (TCG_TARGET_REG_BITS == 64) { + tcg_out_opc_imm(s, sd1, lo, base, 0); + tcg_out_opc_imm(s, sd2, lo, base, 7); + } else { + tcg_out_opc_imm(s, sw1, MIPS_BE ? hi : lo, base, 0 + 0); + tcg_out_opc_imm(s, sw2, MIPS_BE ? hi : lo, base, 0 + 3); + tcg_out_opc_imm(s, sw1, MIPS_BE ? lo : hi, base, 4 + 0); + tcg_out_opc_imm(s, sw2, MIPS_BE ? lo : hi, base, 4 + 3); + } + break; + + default: + tcg_abort(); + } +} static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args, bool is_64) { TCGReg addr_regl, addr_regh __attribute__((unused)); @@ -1540,6 +1841,8 @@ static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args, bool is_64) MemOp opc; #if defined(CONFIG_SOFTMMU) tcg_insn_unit *label_ptr[2]; +#else + unsigned a_bits, s_bits; #endif TCGReg base = TCG_REG_A0; @@ -1558,7 +1861,6 @@ static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args, bool is_64) data_regl, data_regh, addr_regl, addr_regh, s->code_ptr, label_ptr); #else - base = TCG_REG_A0; if (TCG_TARGET_REG_BITS > TARGET_LONG_BITS) { tcg_out_ext32u(s, base, addr_regl); addr_regl = base; @@ -1570,7 +1872,27 @@ static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args, bool is_64) } else { tcg_out_opc_reg(s, ALIAS_PADD, base, TCG_GUEST_BASE_REG, addr_regl); } - tcg_out_qemu_st_direct(s, data_regl, data_regh, base, opc); + a_bits = get_alignment_bits(opc); + s_bits = opc & MO_SIZE; + /* + * R6 removes the left/right instructions but requires the + * system to support misaligned memory accesses. + */ + if (use_mips32r6_instructions) { + if (a_bits) { + tcg_out_test_alignment(s, true, addr_regl, addr_regh, a_bits); + } + tcg_out_qemu_st_direct(s, data_regl, data_regh, base, opc); + } else { + if (a_bits && a_bits != s_bits) { + tcg_out_test_alignment(s, true, addr_regl, addr_regh, a_bits); + } + if (a_bits >= s_bits) { + tcg_out_qemu_st_direct(s, data_regl, data_regh, base, opc); + } else { + tcg_out_qemu_st_unalign(s, data_regl, data_regh, base, opc); + } + } #endif } diff --git a/tcg/mips/tcg-target.h b/tcg/mips/tcg-target.h index c366fdf74b..7669213175 100644 --- a/tcg/mips/tcg-target.h +++ b/tcg/mips/tcg-target.h @@ -207,8 +207,6 @@ extern bool use_mips32r2_instructions; void tb_target_set_jmp_target(uintptr_t, uintptr_t, uintptr_t, uintptr_t) QEMU_ERROR("code path is reachable"); -#ifdef CONFIG_SOFTMMU #define TCG_TARGET_NEED_LDST_LABELS -#endif #endif From d9e52834656ffa766113f1fbae8efe37aaac1df2 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 6 Aug 2021 06:55:14 -1000 Subject: [PATCH 319/460] tcg/mips: Support unaligned access for softmmu MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We can use the routines just added for user-only to emit unaligned accesses in softmmu mode too. Tested-by: Jiaxun Yang Reviewed-by: Jiaxun Yang Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/mips/tcg-target.c.inc | 91 ++++++++++++++++++++++----------------- 1 file changed, 51 insertions(+), 40 deletions(-) diff --git a/tcg/mips/tcg-target.c.inc b/tcg/mips/tcg-target.c.inc index 2c94ac2ed6..993149d18a 100644 --- a/tcg/mips/tcg-target.c.inc +++ b/tcg/mips/tcg-target.c.inc @@ -1134,8 +1134,10 @@ static void tcg_out_tlb_load(TCGContext *s, TCGReg base, TCGReg addrl, tcg_insn_unit *label_ptr[2], bool is_load) { MemOp opc = get_memop(oi); - unsigned s_bits = opc & MO_SIZE; unsigned a_bits = get_alignment_bits(opc); + unsigned s_bits = opc & MO_SIZE; + unsigned a_mask = (1 << a_bits) - 1; + unsigned s_mask = (1 << s_bits) - 1; int mem_index = get_mmuidx(oi); int fast_off = TLB_MASK_TABLE_OFS(mem_index); int mask_off = fast_off + offsetof(CPUTLBDescFast, mask); @@ -1143,7 +1145,7 @@ static void tcg_out_tlb_load(TCGContext *s, TCGReg base, TCGReg addrl, int add_off = offsetof(CPUTLBEntry, addend); int cmp_off = (is_load ? offsetof(CPUTLBEntry, addr_read) : offsetof(CPUTLBEntry, addr_write)); - target_ulong mask; + target_ulong tlb_mask; /* Load tlb_mask[mmu_idx] and tlb_table[mmu_idx]. */ tcg_out_ld(s, TCG_TYPE_PTR, TCG_TMP0, TCG_AREG0, mask_off); @@ -1157,27 +1159,13 @@ static void tcg_out_tlb_load(TCGContext *s, TCGReg base, TCGReg addrl, /* Add the tlb_table pointer, creating the CPUTLBEntry address in TMP3. */ tcg_out_opc_reg(s, ALIAS_PADD, TCG_TMP3, TCG_TMP3, TCG_TMP1); - /* We don't currently support unaligned accesses. - We could do so with mips32r6. */ - if (a_bits < s_bits) { - a_bits = s_bits; - } - - /* Mask the page bits, keeping the alignment bits to compare against. */ - mask = (target_ulong)TARGET_PAGE_MASK | ((1 << a_bits) - 1); - /* Load the (low-half) tlb comparator. */ if (TCG_TARGET_REG_BITS < TARGET_LONG_BITS) { - tcg_out_ld(s, TCG_TYPE_I32, TCG_TMP0, TCG_TMP3, cmp_off + LO_OFF); - tcg_out_movi(s, TCG_TYPE_I32, TCG_TMP1, mask); + tcg_out_ldst(s, OPC_LW, TCG_TMP0, TCG_TMP3, cmp_off + LO_OFF); } else { tcg_out_ldst(s, (TARGET_LONG_BITS == 64 ? OPC_LD : TCG_TARGET_REG_BITS == 64 ? OPC_LWU : OPC_LW), TCG_TMP0, TCG_TMP3, cmp_off); - tcg_out_movi(s, TCG_TYPE_TL, TCG_TMP1, mask); - /* No second compare is required here; - load the tlb addend for the fast path. */ - tcg_out_ld(s, TCG_TYPE_PTR, TCG_TMP2, TCG_TMP3, add_off); } /* Zero extend a 32-bit guest address for a 64-bit host. */ @@ -1185,7 +1173,25 @@ static void tcg_out_tlb_load(TCGContext *s, TCGReg base, TCGReg addrl, tcg_out_ext32u(s, base, addrl); addrl = base; } - tcg_out_opc_reg(s, OPC_AND, TCG_TMP1, TCG_TMP1, addrl); + + /* + * Mask the page bits, keeping the alignment bits to compare against. + * For unaligned accesses, compare against the end of the access to + * verify that it does not cross a page boundary. + */ + tlb_mask = (target_ulong)TARGET_PAGE_MASK | a_mask; + tcg_out_movi(s, TCG_TYPE_I32, TCG_TMP1, tlb_mask); + if (a_mask >= s_mask) { + tcg_out_opc_reg(s, OPC_AND, TCG_TMP1, TCG_TMP1, addrl); + } else { + tcg_out_opc_imm(s, ALIAS_PADDI, TCG_TMP2, addrl, s_mask - a_mask); + tcg_out_opc_reg(s, OPC_AND, TCG_TMP1, TCG_TMP1, TCG_TMP2); + } + + if (TCG_TARGET_REG_BITS >= TARGET_LONG_BITS) { + /* Load the tlb addend for the fast path. */ + tcg_out_ld(s, TCG_TYPE_PTR, TCG_TMP2, TCG_TMP3, add_off); + } label_ptr[0] = s->code_ptr; tcg_out_opc_br(s, OPC_BNE, TCG_TMP1, TCG_TMP0); @@ -1193,7 +1199,7 @@ static void tcg_out_tlb_load(TCGContext *s, TCGReg base, TCGReg addrl, /* Load and test the high half tlb comparator. */ if (TCG_TARGET_REG_BITS < TARGET_LONG_BITS) { /* delay slot */ - tcg_out_ld(s, TCG_TYPE_I32, TCG_TMP0, TCG_TMP3, cmp_off + HI_OFF); + tcg_out_ldst(s, OPC_LW, TCG_TMP0, TCG_TMP3, cmp_off + HI_OFF); /* Load the tlb addend for the fast path. */ tcg_out_ld(s, TCG_TYPE_PTR, TCG_TMP2, TCG_TMP3, add_off); @@ -1515,8 +1521,7 @@ static void tcg_out_qemu_ld_direct(TCGContext *s, TCGReg lo, TCGReg hi, } } -static void __attribute__((unused)) -tcg_out_qemu_ld_unalign(TCGContext *s, TCGReg lo, TCGReg hi, +static void tcg_out_qemu_ld_unalign(TCGContext *s, TCGReg lo, TCGReg hi, TCGReg base, MemOp opc, bool is_64) { const MIPSInsn lw1 = MIPS_BE ? OPC_LWL : OPC_LWR; @@ -1645,8 +1650,8 @@ static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, bool is_64) #if defined(CONFIG_SOFTMMU) tcg_insn_unit *label_ptr[2]; #else - unsigned a_bits, s_bits; #endif + unsigned a_bits, s_bits; TCGReg base = TCG_REG_A0; data_regl = *args++; @@ -1655,10 +1660,20 @@ static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, bool is_64) addr_regh = (TCG_TARGET_REG_BITS < TARGET_LONG_BITS ? *args++ : 0); oi = *args++; opc = get_memop(oi); + a_bits = get_alignment_bits(opc); + s_bits = opc & MO_SIZE; + /* + * R6 removes the left/right instructions but requires the + * system to support misaligned memory accesses. + */ #if defined(CONFIG_SOFTMMU) tcg_out_tlb_load(s, base, addr_regl, addr_regh, oi, label_ptr, 1); - tcg_out_qemu_ld_direct(s, data_regl, data_regh, base, opc, is_64); + if (use_mips32r6_instructions || a_bits >= s_bits) { + tcg_out_qemu_ld_direct(s, data_regl, data_regh, base, opc, is_64); + } else { + tcg_out_qemu_ld_unalign(s, data_regl, data_regh, base, opc, is_64); + } add_qemu_ldst_label(s, 1, oi, (is_64 ? TCG_TYPE_I64 : TCG_TYPE_I32), data_regl, data_regh, addr_regl, addr_regh, @@ -1675,12 +1690,6 @@ static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, bool is_64) } else { tcg_out_opc_reg(s, ALIAS_PADD, base, TCG_GUEST_BASE_REG, addr_regl); } - a_bits = get_alignment_bits(opc); - s_bits = opc & MO_SIZE; - /* - * R6 removes the left/right instructions but requires the - * system to support misaligned memory accesses. - */ if (use_mips32r6_instructions) { if (a_bits) { tcg_out_test_alignment(s, true, addr_regl, addr_regh, a_bits); @@ -1760,8 +1769,7 @@ static void tcg_out_qemu_st_direct(TCGContext *s, TCGReg lo, TCGReg hi, } } -static void __attribute__((unused)) -tcg_out_qemu_st_unalign(TCGContext *s, TCGReg lo, TCGReg hi, +static void tcg_out_qemu_st_unalign(TCGContext *s, TCGReg lo, TCGReg hi, TCGReg base, MemOp opc) { const MIPSInsn sw1 = MIPS_BE ? OPC_SWL : OPC_SWR; @@ -1841,9 +1849,8 @@ static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args, bool is_64) MemOp opc; #if defined(CONFIG_SOFTMMU) tcg_insn_unit *label_ptr[2]; -#else - unsigned a_bits, s_bits; #endif + unsigned a_bits, s_bits; TCGReg base = TCG_REG_A0; data_regl = *args++; @@ -1852,10 +1859,20 @@ static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args, bool is_64) addr_regh = (TCG_TARGET_REG_BITS < TARGET_LONG_BITS ? *args++ : 0); oi = *args++; opc = get_memop(oi); + a_bits = get_alignment_bits(opc); + s_bits = opc & MO_SIZE; + /* + * R6 removes the left/right instructions but requires the + * system to support misaligned memory accesses. + */ #if defined(CONFIG_SOFTMMU) tcg_out_tlb_load(s, base, addr_regl, addr_regh, oi, label_ptr, 0); - tcg_out_qemu_st_direct(s, data_regl, data_regh, base, opc); + if (use_mips32r6_instructions || a_bits >= s_bits) { + tcg_out_qemu_st_direct(s, data_regl, data_regh, base, opc); + } else { + tcg_out_qemu_st_unalign(s, data_regl, data_regh, base, opc); + } add_qemu_ldst_label(s, 0, oi, (is_64 ? TCG_TYPE_I64 : TCG_TYPE_I32), data_regl, data_regh, addr_regl, addr_regh, @@ -1872,12 +1889,6 @@ static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args, bool is_64) } else { tcg_out_opc_reg(s, ALIAS_PADD, base, TCG_GUEST_BASE_REG, addr_regl); } - a_bits = get_alignment_bits(opc); - s_bits = opc & MO_SIZE; - /* - * R6 removes the left/right instructions but requires the - * system to support misaligned memory accesses. - */ if (use_mips32r6_instructions) { if (a_bits) { tcg_out_test_alignment(s, true, addr_regl, addr_regh, a_bits); From 414399b6b8a02f03d3b0cba5cfa9205dff618a9b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 8 Feb 2022 10:15:58 +1100 Subject: [PATCH 320/460] tcg/sparc: Use tcg_out_movi_imm13 in tcg_out_addsub2_i64 When BH is constant, it is constrained to 11 bits for use in MOVCC. For the cases in which we must load the constant BH into a register, we do not need the full logic of tcg_out_movi; we can use the simpler function for emitting a 13 bit constant. This eliminates the only case in which TCG_REG_T2 was passed to tcg_out_movi, which will shortly become invalid. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- tcg/sparc/tcg-target.c.inc | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/tcg/sparc/tcg-target.c.inc b/tcg/sparc/tcg-target.c.inc index 0c062c60eb..8d5992ef29 100644 --- a/tcg/sparc/tcg-target.c.inc +++ b/tcg/sparc/tcg-target.c.inc @@ -795,7 +795,7 @@ static void tcg_out_addsub2_i64(TCGContext *s, TCGReg rl, TCGReg rh, if (use_vis3_instructions && !is_sub) { /* Note that ADDXC doesn't accept immediates. */ if (bhconst && bh != 0) { - tcg_out_movi(s, TCG_TYPE_I64, TCG_REG_T2, bh); + tcg_out_movi_imm13(s, TCG_REG_T2, bh); bh = TCG_REG_T2; } tcg_out_arith(s, rh, ah, bh, ARITH_ADDXC); @@ -811,9 +811,13 @@ static void tcg_out_addsub2_i64(TCGContext *s, TCGReg rl, TCGReg rh, tcg_out_movcc(s, TCG_COND_GEU, MOVCC_XCC, rh, ah, 0); } } else { - /* Otherwise adjust BH as if there is carry into T2 ... */ + /* + * Otherwise adjust BH as if there is carry into T2. + * Note that constant BH is constrained to 11 bits for the MOVCC, + * so the adjustment fits 12 bits. + */ if (bhconst) { - tcg_out_movi(s, TCG_TYPE_I64, TCG_REG_T2, bh + (is_sub ? -1 : 1)); + tcg_out_movi_imm13(s, TCG_REG_T2, bh + (is_sub ? -1 : 1)); } else { tcg_out_arithi(s, TCG_REG_T2, bh, 1, is_sub ? ARITH_SUB : ARITH_ADD); From c71929c345d08fa70edb1eeaa9babaaf5d6a9bf7 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 8 Feb 2022 10:46:40 +1100 Subject: [PATCH 321/460] tcg/sparc: Split out tcg_out_movi_imm32 Handle 32-bit constants with a separate function, so that tcg_out_movi_int does not need to recurse. This slightly rearranges the order of tests for small constants, but produces the same output. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- tcg/sparc/tcg-target.c.inc | 36 +++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/tcg/sparc/tcg-target.c.inc b/tcg/sparc/tcg-target.c.inc index 8d5992ef29..2f7c8dcb0a 100644 --- a/tcg/sparc/tcg-target.c.inc +++ b/tcg/sparc/tcg-target.c.inc @@ -413,15 +413,30 @@ static void tcg_out_movi_imm13(TCGContext *s, TCGReg ret, int32_t arg) tcg_out_arithi(s, ret, TCG_REG_G0, arg, ARITH_OR); } +static void tcg_out_movi_imm32(TCGContext *s, TCGReg ret, int32_t arg) +{ + if (check_fit_i32(arg, 13)) { + /* A 13-bit constant sign-extended to 64-bits. */ + tcg_out_movi_imm13(s, ret, arg); + } else { + /* A 32-bit constant zero-extended to 64 bits. */ + tcg_out_sethi(s, ret, arg); + if (arg & 0x3ff) { + tcg_out_arithi(s, ret, ret, arg & 0x3ff, ARITH_OR); + } + } +} + static void tcg_out_movi_int(TCGContext *s, TCGType type, TCGReg ret, tcg_target_long arg, bool in_prologue) { tcg_target_long hi, lo = (int32_t)arg; tcg_target_long test, lsb; - /* Make sure we test 32-bit constants for imm13 properly. */ - if (type == TCG_TYPE_I32) { - arg = lo; + /* A 32-bit constant, or 32-bit zero-extended to 64-bits. */ + if (type == TCG_TYPE_I32 || arg == (uint32_t)arg) { + tcg_out_movi_imm32(s, ret, arg); + return; } /* A 13-bit constant sign-extended to 64-bits. */ @@ -439,15 +454,6 @@ static void tcg_out_movi_int(TCGContext *s, TCGType type, TCGReg ret, } } - /* A 32-bit constant, or 32-bit zero-extended to 64-bits. */ - if (type == TCG_TYPE_I32 || arg == (uint32_t)arg) { - tcg_out_sethi(s, ret, arg); - if (arg & 0x3ff) { - tcg_out_arithi(s, ret, ret, arg & 0x3ff, ARITH_OR); - } - return; - } - /* A 32-bit constant sign-extended to 64-bits. */ if (arg == lo) { tcg_out_sethi(s, ret, ~arg); @@ -471,13 +477,13 @@ static void tcg_out_movi_int(TCGContext *s, TCGType type, TCGReg ret, /* A 64-bit constant decomposed into 2 32-bit pieces. */ if (check_fit_i32(lo, 13)) { hi = (arg - lo) >> 32; - tcg_out_movi(s, TCG_TYPE_I32, ret, hi); + tcg_out_movi_imm32(s, ret, hi); tcg_out_arithi(s, ret, ret, 32, SHIFT_SLLX); tcg_out_arithi(s, ret, ret, lo, ARITH_ADD); } else { hi = arg >> 32; - tcg_out_movi(s, TCG_TYPE_I32, ret, hi); - tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_T2, lo); + tcg_out_movi_imm32(s, ret, hi); + tcg_out_movi_imm32(s, TCG_REG_T2, lo); tcg_out_arithi(s, ret, ret, 32, SHIFT_SLLX); tcg_out_arith(s, ret, ret, TCG_REG_T2, ARITH_OR); } From 92840d06faeed2038b90d5b168c18d73ca3a44b8 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 5 Aug 2021 02:16:05 +0300 Subject: [PATCH 322/460] tcg/sparc: Add scratch argument to tcg_out_movi_int This will allow us to control exactly what scratch register is used for loading the constant. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- tcg/sparc/tcg-target.c.inc | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/tcg/sparc/tcg-target.c.inc b/tcg/sparc/tcg-target.c.inc index 2f7c8dcb0a..7a8f20ee9a 100644 --- a/tcg/sparc/tcg-target.c.inc +++ b/tcg/sparc/tcg-target.c.inc @@ -428,7 +428,8 @@ static void tcg_out_movi_imm32(TCGContext *s, TCGReg ret, int32_t arg) } static void tcg_out_movi_int(TCGContext *s, TCGType type, TCGReg ret, - tcg_target_long arg, bool in_prologue) + tcg_target_long arg, bool in_prologue, + TCGReg scratch) { tcg_target_long hi, lo = (int32_t)arg; tcg_target_long test, lsb; @@ -483,16 +484,17 @@ static void tcg_out_movi_int(TCGContext *s, TCGType type, TCGReg ret, } else { hi = arg >> 32; tcg_out_movi_imm32(s, ret, hi); - tcg_out_movi_imm32(s, TCG_REG_T2, lo); + tcg_out_movi_imm32(s, scratch, lo); tcg_out_arithi(s, ret, ret, 32, SHIFT_SLLX); - tcg_out_arith(s, ret, ret, TCG_REG_T2, ARITH_OR); + tcg_out_arith(s, ret, ret, scratch, ARITH_OR); } } static void tcg_out_movi(TCGContext *s, TCGType type, TCGReg ret, tcg_target_long arg) { - tcg_out_movi_int(s, type, ret, arg, false); + tcg_debug_assert(ret != TCG_REG_T2); + tcg_out_movi_int(s, type, ret, arg, false, TCG_REG_T2); } static void tcg_out_ldst_rr(TCGContext *s, TCGReg data, TCGReg a1, @@ -847,7 +849,7 @@ static void tcg_out_call_nodelay(TCGContext *s, const tcg_insn_unit *dest, } else { uintptr_t desti = (uintptr_t)dest; tcg_out_movi_int(s, TCG_TYPE_PTR, TCG_REG_T1, - desti & ~0xfff, in_prologue); + desti & ~0xfff, in_prologue, TCG_REG_O7); tcg_out_arithi(s, TCG_REG_O7, TCG_REG_T1, desti & 0xfff, JMPL); } } @@ -1023,7 +1025,8 @@ static void tcg_target_qemu_prologue(TCGContext *s) #ifndef CONFIG_SOFTMMU if (guest_base != 0) { - tcg_out_movi_int(s, TCG_TYPE_PTR, TCG_GUEST_BASE_REG, guest_base, true); + tcg_out_movi_int(s, TCG_TYPE_PTR, TCG_GUEST_BASE_REG, + guest_base, true, TCG_REG_T1); tcg_regset_set_reg(s->reserved_regs, TCG_GUEST_BASE_REG); } #endif From 684db2a0b04ff024d0a275de85982fe5892185d3 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 5 Aug 2021 04:09:15 +0300 Subject: [PATCH 323/460] tcg/sparc: Improve code gen for shifted 32-bit constants MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We had code for checking for 13 and 21-bit shifted constants, but we can do better and allow 32-bit shifted constants. This is still 2 insns shorter than the full 64-bit sequence. Reviewed-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/sparc/tcg-target.c.inc | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tcg/sparc/tcg-target.c.inc b/tcg/sparc/tcg-target.c.inc index 7a8f20ee9a..ed2f4ecc40 100644 --- a/tcg/sparc/tcg-target.c.inc +++ b/tcg/sparc/tcg-target.c.inc @@ -462,17 +462,17 @@ static void tcg_out_movi_int(TCGContext *s, TCGType type, TCGReg ret, return; } - /* A 21-bit constant, shifted. */ + /* A 32-bit constant, shifted. */ lsb = ctz64(arg); test = (tcg_target_long)arg >> lsb; - if (check_fit_tl(test, 13)) { - tcg_out_movi_imm13(s, ret, test); - tcg_out_arithi(s, ret, ret, lsb, SHIFT_SLLX); - return; - } else if (lsb > 10 && test == extract64(test, 0, 21)) { + if (lsb > 10 && test == extract64(test, 0, 21)) { tcg_out_sethi(s, ret, test << 10); tcg_out_arithi(s, ret, ret, lsb - 10, SHIFT_SLLX); return; + } else if (test == (uint32_t)test || test == (int32_t)test) { + tcg_out_movi_int(s, TCG_TYPE_I64, ret, test, in_prologue, scratch); + tcg_out_arithi(s, ret, ret, lsb, SHIFT_SLLX); + return; } /* A 64-bit constant decomposed into 2 32-bit pieces. */ From 6a6bfa3c60ba70e870f8355b9d3508915c90c13a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 5 Feb 2022 08:12:30 +0300 Subject: [PATCH 324/460] tcg/sparc: Convert patch_reloc to return bool MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since 7ecd02a06f8, if patch_reloc fails we restart translation with a smaller TB. SPARC had its function signature changed, but not the logic. Replace assert with return false. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- tcg/sparc/tcg-target.c.inc | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tcg/sparc/tcg-target.c.inc b/tcg/sparc/tcg-target.c.inc index ed2f4ecc40..213aba4be6 100644 --- a/tcg/sparc/tcg-target.c.inc +++ b/tcg/sparc/tcg-target.c.inc @@ -323,12 +323,16 @@ static bool patch_reloc(tcg_insn_unit *src_rw, int type, switch (type) { case R_SPARC_WDISP16: - assert(check_fit_ptr(pcrel >> 2, 16)); + if (!check_fit_ptr(pcrel >> 2, 16)) { + return false; + } insn &= ~INSN_OFF16(-1); insn |= INSN_OFF16(pcrel); break; case R_SPARC_WDISP19: - assert(check_fit_ptr(pcrel >> 2, 19)); + if (!check_fit_ptr(pcrel >> 2, 19)) { + return false; + } insn &= ~INSN_OFF19(-1); insn |= INSN_OFF19(pcrel); break; From c834b8d81b0ee597d185a4d8127dfc76230307c8 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 5 Aug 2021 04:34:20 +0300 Subject: [PATCH 325/460] tcg/sparc: Use the constant pool for 64-bit constants Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- tcg/sparc/tcg-target.c.inc | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tcg/sparc/tcg-target.c.inc b/tcg/sparc/tcg-target.c.inc index 213aba4be6..e78945d153 100644 --- a/tcg/sparc/tcg-target.c.inc +++ b/tcg/sparc/tcg-target.c.inc @@ -336,6 +336,13 @@ static bool patch_reloc(tcg_insn_unit *src_rw, int type, insn &= ~INSN_OFF19(-1); insn |= INSN_OFF19(pcrel); break; + case R_SPARC_13: + if (!check_fit_ptr(value, 13)) { + return false; + } + insn &= ~INSN_IMM13(-1); + insn |= INSN_IMM13(value); + break; default: g_assert_not_reached(); } @@ -479,6 +486,14 @@ static void tcg_out_movi_int(TCGContext *s, TCGType type, TCGReg ret, return; } + /* Use the constant pool, if possible. */ + if (!in_prologue && USE_REG_TB) { + new_pool_label(s, arg, R_SPARC_13, s->code_ptr, + tcg_tbrel_diff(s, NULL)); + tcg_out32(s, LDX | INSN_RD(ret) | INSN_RS1(TCG_REG_TB)); + return; + } + /* A 64-bit constant decomposed into 2 32-bit pieces. */ if (check_fit_i32(lo, 13)) { hi = (arg - lo) >> 32; From e01d60f211251388a5d9ae7e02d0b4500af26966 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 5 Aug 2021 02:23:05 +0300 Subject: [PATCH 326/460] tcg/sparc: Add tcg_out_jmpl_const for better tail calls Due to mapping changes, we now rarely place the code_gen_buffer near the main executable. Which means that direct calls will now rarely be in range. So, always use indirect calls for tail calls, which allows us to avoid clobbering %o7, and therefore we need not save and restore it. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- tcg/sparc/tcg-target.c.inc | 37 +++++++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/tcg/sparc/tcg-target.c.inc b/tcg/sparc/tcg-target.c.inc index e78945d153..646bb462c3 100644 --- a/tcg/sparc/tcg-target.c.inc +++ b/tcg/sparc/tcg-target.c.inc @@ -858,6 +858,19 @@ static void tcg_out_addsub2_i64(TCGContext *s, TCGReg rl, TCGReg rh, tcg_out_mov(s, TCG_TYPE_I64, rl, tmp); } +static void tcg_out_jmpl_const(TCGContext *s, const tcg_insn_unit *dest, + bool in_prologue, bool tail_call) +{ + uintptr_t desti = (uintptr_t)dest; + + /* Be careful not to clobber %o7 for a tail call. */ + tcg_out_movi_int(s, TCG_TYPE_PTR, TCG_REG_T1, + desti & ~0xfff, in_prologue, + tail_call ? TCG_REG_G2 : TCG_REG_O7); + tcg_out_arithi(s, tail_call ? TCG_REG_G0 : TCG_REG_O7, + TCG_REG_T1, desti & 0xfff, JMPL); +} + static void tcg_out_call_nodelay(TCGContext *s, const tcg_insn_unit *dest, bool in_prologue) { @@ -866,10 +879,7 @@ static void tcg_out_call_nodelay(TCGContext *s, const tcg_insn_unit *dest, if (disp == (int32_t)disp) { tcg_out32(s, CALL | (uint32_t)disp >> 2); } else { - uintptr_t desti = (uintptr_t)dest; - tcg_out_movi_int(s, TCG_TYPE_PTR, TCG_REG_T1, - desti & ~0xfff, in_prologue, TCG_REG_O7); - tcg_out_arithi(s, TCG_REG_O7, TCG_REG_T1, desti & 0xfff, JMPL); + tcg_out_jmpl_const(s, dest, in_prologue, false); } } @@ -960,11 +970,10 @@ static void build_trampolines(TCGContext *s) /* Set the retaddr operand. */ tcg_out_mov(s, TCG_TYPE_PTR, ra, TCG_REG_O7); - /* Set the env operand. */ - tcg_out_mov(s, TCG_TYPE_PTR, TCG_REG_O0, TCG_AREG0); /* Tail call. */ - tcg_out_call_nodelay(s, qemu_ld_helpers[i], true); - tcg_out_mov(s, TCG_TYPE_PTR, TCG_REG_O7, ra); + tcg_out_jmpl_const(s, qemu_ld_helpers[i], true, true); + /* delay slot -- set the env argument */ + tcg_out_mov_delay(s, TCG_REG_O0, TCG_AREG0); } for (i = 0; i < ARRAY_SIZE(qemu_st_helpers); ++i) { @@ -1006,14 +1015,14 @@ static void build_trampolines(TCGContext *s) if (ra >= TCG_REG_O6) { tcg_out_st(s, TCG_TYPE_PTR, TCG_REG_O7, TCG_REG_CALL_STACK, TCG_TARGET_CALL_STACK_OFFSET); - ra = TCG_REG_G1; + } else { + tcg_out_mov(s, TCG_TYPE_PTR, ra, TCG_REG_O7); } - tcg_out_mov(s, TCG_TYPE_PTR, ra, TCG_REG_O7); - /* Set the env operand. */ - tcg_out_mov(s, TCG_TYPE_PTR, TCG_REG_O0, TCG_AREG0); + /* Tail call. */ - tcg_out_call_nodelay(s, qemu_st_helpers[i], true); - tcg_out_mov(s, TCG_TYPE_PTR, TCG_REG_O7, ra); + tcg_out_jmpl_const(s, qemu_st_helpers[i], true, true); + /* delay slot -- set the env argument */ + tcg_out_mov_delay(s, TCG_REG_O0, TCG_AREG0); } } #endif From 321dbde33a6aa8e7780a3b6b4746628d215a1fec Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 5 Aug 2021 03:54:30 +0300 Subject: [PATCH 327/460] tcg/sparc: Support unaligned access for user-only This is kinda sorta the opposite of the other tcg hosts, where we get (normal) alignment checks for free with host SIGBUS and need to add code to support unaligned accesses. This inline code expansion is somewhat large, but it takes quite a few instructions to make a function call to a helper anyway. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- tcg/sparc/tcg-target.c.inc | 219 +++++++++++++++++++++++++++++++++++-- 1 file changed, 211 insertions(+), 8 deletions(-) diff --git a/tcg/sparc/tcg-target.c.inc b/tcg/sparc/tcg-target.c.inc index 646bb462c3..72d9552fd0 100644 --- a/tcg/sparc/tcg-target.c.inc +++ b/tcg/sparc/tcg-target.c.inc @@ -211,6 +211,7 @@ static const int tcg_target_call_oarg_regs[] = { #define ARITH_ADD (INSN_OP(2) | INSN_OP3(0x00)) #define ARITH_ADDCC (INSN_OP(2) | INSN_OP3(0x10)) #define ARITH_AND (INSN_OP(2) | INSN_OP3(0x01)) +#define ARITH_ANDCC (INSN_OP(2) | INSN_OP3(0x11)) #define ARITH_ANDN (INSN_OP(2) | INSN_OP3(0x05)) #define ARITH_OR (INSN_OP(2) | INSN_OP3(0x02)) #define ARITH_ORCC (INSN_OP(2) | INSN_OP3(0x12)) @@ -1025,6 +1026,38 @@ static void build_trampolines(TCGContext *s) tcg_out_mov_delay(s, TCG_REG_O0, TCG_AREG0); } } +#else +static const tcg_insn_unit *qemu_unalign_ld_trampoline; +static const tcg_insn_unit *qemu_unalign_st_trampoline; + +static void build_trampolines(TCGContext *s) +{ + for (int ld = 0; ld < 2; ++ld) { + void *helper; + + while ((uintptr_t)s->code_ptr & 15) { + tcg_out_nop(s); + } + + if (ld) { + helper = helper_unaligned_ld; + qemu_unalign_ld_trampoline = tcg_splitwx_to_rx(s->code_ptr); + } else { + helper = helper_unaligned_st; + qemu_unalign_st_trampoline = tcg_splitwx_to_rx(s->code_ptr); + } + + if (!SPARC64 && TARGET_LONG_BITS == 64) { + /* Install the high part of the address. */ + tcg_out_arithi(s, TCG_REG_O1, TCG_REG_O2, 32, SHIFT_SRLX); + } + + /* Tail call. */ + tcg_out_jmpl_const(s, helper, true, true); + /* delay slot -- set the env argument */ + tcg_out_mov_delay(s, TCG_REG_O0, TCG_AREG0); + } +} #endif /* Generate global QEMU prologue and epilogue code */ @@ -1075,9 +1108,7 @@ static void tcg_target_qemu_prologue(TCGContext *s) /* delay slot */ tcg_out_movi_imm13(s, TCG_REG_O0, 0); -#ifdef CONFIG_SOFTMMU build_trampolines(s); -#endif } static void tcg_out_nop_fill(tcg_insn_unit *p, int count) @@ -1162,18 +1193,22 @@ static TCGReg tcg_out_tlb_load(TCGContext *s, TCGReg addr, int mem_index, static const int qemu_ld_opc[(MO_SSIZE | MO_BSWAP) + 1] = { [MO_UB] = LDUB, [MO_SB] = LDSB, + [MO_UB | MO_LE] = LDUB, + [MO_SB | MO_LE] = LDSB, [MO_BEUW] = LDUH, [MO_BESW] = LDSH, [MO_BEUL] = LDUW, [MO_BESL] = LDSW, [MO_BEUQ] = LDX, + [MO_BESQ] = LDX, [MO_LEUW] = LDUH_LE, [MO_LESW] = LDSH_LE, [MO_LEUL] = LDUW_LE, [MO_LESL] = LDSW_LE, [MO_LEUQ] = LDX_LE, + [MO_LESQ] = LDX_LE, }; static const int qemu_st_opc[(MO_SIZE | MO_BSWAP) + 1] = { @@ -1192,11 +1227,12 @@ static void tcg_out_qemu_ld(TCGContext *s, TCGReg data, TCGReg addr, MemOpIdx oi, bool is_64) { MemOp memop = get_memop(oi); + tcg_insn_unit *label_ptr; + #ifdef CONFIG_SOFTMMU unsigned memi = get_mmuidx(oi); TCGReg addrz, param; const tcg_insn_unit *func; - tcg_insn_unit *label_ptr; addrz = tcg_out_tlb_load(s, addr, memi, memop, offsetof(CPUTLBEntry, addr_read)); @@ -1260,13 +1296,99 @@ static void tcg_out_qemu_ld(TCGContext *s, TCGReg data, TCGReg addr, *label_ptr |= INSN_OFF19(tcg_ptr_byte_diff(s->code_ptr, label_ptr)); #else + TCGReg index = (guest_base ? TCG_GUEST_BASE_REG : TCG_REG_G0); + unsigned a_bits = get_alignment_bits(memop); + unsigned s_bits = memop & MO_SIZE; + unsigned t_bits; + if (SPARC64 && TARGET_LONG_BITS == 32) { tcg_out_arithi(s, TCG_REG_T1, addr, 0, SHIFT_SRL); addr = TCG_REG_T1; } - tcg_out_ldst_rr(s, data, addr, - (guest_base ? TCG_GUEST_BASE_REG : TCG_REG_G0), + + /* + * Normal case: alignment equal to access size. + */ + if (a_bits == s_bits) { + tcg_out_ldst_rr(s, data, addr, index, + qemu_ld_opc[memop & (MO_BSWAP | MO_SSIZE)]); + return; + } + + /* + * Test for at least natural alignment, and assume most accesses + * will be aligned -- perform a straight load in the delay slot. + * This is required to preserve atomicity for aligned accesses. + */ + t_bits = MAX(a_bits, s_bits); + tcg_debug_assert(t_bits < 13); + tcg_out_arithi(s, TCG_REG_G0, addr, (1u << t_bits) - 1, ARITH_ANDCC); + + /* beq,a,pt %icc, label */ + label_ptr = s->code_ptr; + tcg_out_bpcc0(s, COND_E, BPCC_A | BPCC_PT | BPCC_ICC, 0); + /* delay slot */ + tcg_out_ldst_rr(s, data, addr, index, qemu_ld_opc[memop & (MO_BSWAP | MO_SSIZE)]); + + if (a_bits >= s_bits) { + /* + * Overalignment: A successful alignment test will perform the memory + * operation in the delay slot, and failure need only invoke the + * handler for SIGBUS. + */ + TCGReg arg_low = TCG_REG_O1 + (!SPARC64 && TARGET_LONG_BITS == 64); + tcg_out_call_nodelay(s, qemu_unalign_ld_trampoline, false); + /* delay slot -- move to low part of argument reg */ + tcg_out_mov_delay(s, arg_low, addr); + } else { + /* Underalignment: load by pieces of minimum alignment. */ + int ld_opc, a_size, s_size, i; + + /* + * Force full address into T1 early; avoids problems with + * overlap between @addr and @data. + */ + tcg_out_arith(s, TCG_REG_T1, addr, index, ARITH_ADD); + + a_size = 1 << a_bits; + s_size = 1 << s_bits; + if ((memop & MO_BSWAP) == MO_BE) { + ld_opc = qemu_ld_opc[a_bits | MO_BE | (memop & MO_SIGN)]; + tcg_out_ldst(s, data, TCG_REG_T1, 0, ld_opc); + ld_opc = qemu_ld_opc[a_bits | MO_BE]; + for (i = a_size; i < s_size; i += a_size) { + tcg_out_ldst(s, TCG_REG_T2, TCG_REG_T1, i, ld_opc); + tcg_out_arithi(s, data, data, a_size, SHIFT_SLLX); + tcg_out_arith(s, data, data, TCG_REG_T2, ARITH_OR); + } + } else if (a_bits == 0) { + ld_opc = LDUB; + tcg_out_ldst(s, data, TCG_REG_T1, 0, ld_opc); + for (i = a_size; i < s_size; i += a_size) { + if ((memop & MO_SIGN) && i == s_size - a_size) { + ld_opc = LDSB; + } + tcg_out_ldst(s, TCG_REG_T2, TCG_REG_T1, i, ld_opc); + tcg_out_arithi(s, TCG_REG_T2, TCG_REG_T2, i * 8, SHIFT_SLLX); + tcg_out_arith(s, data, data, TCG_REG_T2, ARITH_OR); + } + } else { + ld_opc = qemu_ld_opc[a_bits | MO_LE]; + tcg_out_ldst_rr(s, data, TCG_REG_T1, TCG_REG_G0, ld_opc); + for (i = a_size; i < s_size; i += a_size) { + tcg_out_arithi(s, TCG_REG_T1, TCG_REG_T1, a_size, ARITH_ADD); + if ((memop & MO_SIGN) && i == s_size - a_size) { + ld_opc = qemu_ld_opc[a_bits | MO_LE | MO_SIGN]; + } + tcg_out_ldst_rr(s, TCG_REG_T2, TCG_REG_T1, TCG_REG_G0, ld_opc); + tcg_out_arithi(s, TCG_REG_T2, TCG_REG_T2, i * 8, SHIFT_SLLX); + tcg_out_arith(s, data, data, TCG_REG_T2, ARITH_OR); + } + } + } + + *label_ptr |= INSN_OFF19(tcg_ptr_byte_diff(s->code_ptr, label_ptr)); #endif /* CONFIG_SOFTMMU */ } @@ -1274,11 +1396,12 @@ static void tcg_out_qemu_st(TCGContext *s, TCGReg data, TCGReg addr, MemOpIdx oi) { MemOp memop = get_memop(oi); + tcg_insn_unit *label_ptr; + #ifdef CONFIG_SOFTMMU unsigned memi = get_mmuidx(oi); TCGReg addrz, param; const tcg_insn_unit *func; - tcg_insn_unit *label_ptr; addrz = tcg_out_tlb_load(s, addr, memi, memop, offsetof(CPUTLBEntry, addr_write)); @@ -1315,13 +1438,93 @@ static void tcg_out_qemu_st(TCGContext *s, TCGReg data, TCGReg addr, *label_ptr |= INSN_OFF19(tcg_ptr_byte_diff(s->code_ptr, label_ptr)); #else + TCGReg index = (guest_base ? TCG_GUEST_BASE_REG : TCG_REG_G0); + unsigned a_bits = get_alignment_bits(memop); + unsigned s_bits = memop & MO_SIZE; + unsigned t_bits; + if (SPARC64 && TARGET_LONG_BITS == 32) { tcg_out_arithi(s, TCG_REG_T1, addr, 0, SHIFT_SRL); addr = TCG_REG_T1; } - tcg_out_ldst_rr(s, data, addr, - (guest_base ? TCG_GUEST_BASE_REG : TCG_REG_G0), + + /* + * Normal case: alignment equal to access size. + */ + if (a_bits == s_bits) { + tcg_out_ldst_rr(s, data, addr, index, + qemu_st_opc[memop & (MO_BSWAP | MO_SIZE)]); + return; + } + + /* + * Test for at least natural alignment, and assume most accesses + * will be aligned -- perform a straight store in the delay slot. + * This is required to preserve atomicity for aligned accesses. + */ + t_bits = MAX(a_bits, s_bits); + tcg_debug_assert(t_bits < 13); + tcg_out_arithi(s, TCG_REG_G0, addr, (1u << t_bits) - 1, ARITH_ANDCC); + + /* beq,a,pt %icc, label */ + label_ptr = s->code_ptr; + tcg_out_bpcc0(s, COND_E, BPCC_A | BPCC_PT | BPCC_ICC, 0); + /* delay slot */ + tcg_out_ldst_rr(s, data, addr, index, qemu_st_opc[memop & (MO_BSWAP | MO_SIZE)]); + + if (a_bits >= s_bits) { + /* + * Overalignment: A successful alignment test will perform the memory + * operation in the delay slot, and failure need only invoke the + * handler for SIGBUS. + */ + TCGReg arg_low = TCG_REG_O1 + (!SPARC64 && TARGET_LONG_BITS == 64); + tcg_out_call_nodelay(s, qemu_unalign_st_trampoline, false); + /* delay slot -- move to low part of argument reg */ + tcg_out_mov_delay(s, arg_low, addr); + } else { + /* Underalignment: store by pieces of minimum alignment. */ + int st_opc, a_size, s_size, i; + + /* + * Force full address into T1 early; avoids problems with + * overlap between @addr and @data. + */ + tcg_out_arith(s, TCG_REG_T1, addr, index, ARITH_ADD); + + a_size = 1 << a_bits; + s_size = 1 << s_bits; + if ((memop & MO_BSWAP) == MO_BE) { + st_opc = qemu_st_opc[a_bits | MO_BE]; + for (i = 0; i < s_size; i += a_size) { + TCGReg d = data; + int shift = (s_size - a_size - i) * 8; + if (shift) { + d = TCG_REG_T2; + tcg_out_arithi(s, d, data, shift, SHIFT_SRLX); + } + tcg_out_ldst(s, d, TCG_REG_T1, i, st_opc); + } + } else if (a_bits == 0) { + tcg_out_ldst(s, data, TCG_REG_T1, 0, STB); + for (i = 1; i < s_size; i++) { + tcg_out_arithi(s, TCG_REG_T2, data, i * 8, SHIFT_SRLX); + tcg_out_ldst(s, TCG_REG_T2, TCG_REG_T1, i, STB); + } + } else { + /* Note that ST*A with immediate asi must use indexed address. */ + st_opc = qemu_st_opc[a_bits + MO_LE]; + tcg_out_ldst_rr(s, data, TCG_REG_T1, TCG_REG_G0, st_opc); + for (i = a_size; i < s_size; i += a_size) { + tcg_out_arithi(s, TCG_REG_T2, data, i * 8, SHIFT_SRLX); + tcg_out_arithi(s, TCG_REG_T1, TCG_REG_T1, a_size, ARITH_ADD); + tcg_out_ldst_rr(s, TCG_REG_T2, TCG_REG_T1, TCG_REG_G0, st_opc); + } + } + } + + *label_ptr |= INSN_OFF19(tcg_ptr_byte_diff(s->code_ptr, label_ptr)); #endif /* CONFIG_SOFTMMU */ } From 5c1a101ef6b85537a4ade93c39ea81cadd5c246e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 28 Jul 2021 11:32:24 -1000 Subject: [PATCH 328/460] tests/tcg/multiarch: Add sigbus.c A mostly generic test for unaligned access raising SIGBUS. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- tests/tcg/multiarch/sigbus.c | 68 ++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 tests/tcg/multiarch/sigbus.c diff --git a/tests/tcg/multiarch/sigbus.c b/tests/tcg/multiarch/sigbus.c new file mode 100644 index 0000000000..8134c5fd56 --- /dev/null +++ b/tests/tcg/multiarch/sigbus.c @@ -0,0 +1,68 @@ +#define _GNU_SOURCE 1 + +#include +#include +#include +#include + + +unsigned long long x = 0x8877665544332211ull; +void * volatile p = (void *)&x + 1; + +void sigbus(int sig, siginfo_t *info, void *uc) +{ + assert(sig == SIGBUS); + assert(info->si_signo == SIGBUS); +#ifdef BUS_ADRALN + assert(info->si_code == BUS_ADRALN); +#endif + assert(info->si_addr == p); + exit(EXIT_SUCCESS); +} + +int main() +{ + struct sigaction sa = { + .sa_sigaction = sigbus, + .sa_flags = SA_SIGINFO + }; + int allow_fail = 0; + int tmp; + + tmp = sigaction(SIGBUS, &sa, NULL); + assert(tmp == 0); + + /* + * Select an operation that's likely to enforce alignment. + * On many guests that support unaligned accesses by default, + * this is often an atomic operation. + */ +#if defined(__aarch64__) + asm volatile("ldxr %w0,[%1]" : "=r"(tmp) : "r"(p) : "memory"); +#elif defined(__alpha__) + asm volatile("ldl_l %0,0(%1)" : "=r"(tmp) : "r"(p) : "memory"); +#elif defined(__arm__) + asm volatile("ldrex %0,[%1]" : "=r"(tmp) : "r"(p) : "memory"); +#elif defined(__powerpc__) + asm volatile("lwarx %0,0,%1" : "=r"(tmp) : "r"(p) : "memory"); +#elif defined(__riscv_atomic) + asm volatile("lr.w %0,(%1)" : "=r"(tmp) : "r"(p) : "memory"); +#else + /* No insn known to fault unaligned -- try for a straight load. */ + allow_fail = 1; + tmp = *(volatile int *)p; +#endif + + assert(allow_fail); + + /* + * We didn't see a signal. + * We might as well validate the unaligned load worked. + */ + if (BYTE_ORDER == LITTLE_ENDIAN) { + assert(tmp == 0x55443322); + } else { + assert(tmp == 0x77665544); + } + return EXIT_SUCCESS; +} From 84f54da51349132e59999487cbce79433a5a0e10 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Wed, 9 Feb 2022 09:08:55 +0100 Subject: [PATCH 329/460] target/ppc: Remove 440x4 CPU MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This CPU was partially removed due to lack of support in 2017 by commit aef7796057 ("ppc: remove non implemented cpu models"). Signed-off-by: Fabiano Rosas Reviewed-by: Cédric Le Goater Message-Id: <20220128221611.1221715-1-farosas@linux.ibm.com> Signed-off-by: Cédric Le Goater --- target/ppc/cpu_init.c | 83 ------------------------------------------- 1 file changed, 83 deletions(-) diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index bf60529d37..b4f0835849 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -2703,89 +2703,6 @@ POWERPC_FAMILY(440GP)(ObjectClass *oc, void *data) POWERPC_FLAG_DE | POWERPC_FLAG_BUS_CLK; } -static void init_proc_440x4(CPUPPCState *env) -{ - /* Time base */ - register_tbl(env); - register_BookE_sprs(env, 0x000000000000FFFFULL); - register_440_sprs(env); - register_usprgh_sprs(env); - /* Processor identification */ - spr_register(env, SPR_BOOKE_PIR, "PIR", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_pir, - 0x00000000); - /* XXX : not implemented */ - spr_register(env, SPR_BOOKE_IAC3, "IAC3", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, - 0x00000000); - /* XXX : not implemented */ - spr_register(env, SPR_BOOKE_IAC4, "IAC4", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, - 0x00000000); - /* XXX : not implemented */ - spr_register(env, SPR_BOOKE_DVC1, "DVC1", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, - 0x00000000); - /* XXX : not implemented */ - spr_register(env, SPR_BOOKE_DVC2, "DVC2", - 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->id_tlbs = 0; - env->tlb_type = TLB_EMB; -#endif - init_excp_BookE(env); - env->dcache_line_size = 32; - env->icache_line_size = 32; - /* XXX: TODO: allocate internal IRQ controller */ - - SET_FIT_PERIOD(12, 16, 20, 24); - SET_WDT_PERIOD(20, 24, 28, 32); -} - -POWERPC_FAMILY(440x4)(ObjectClass *oc, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(oc); - PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); - - dc->desc = "PowerPC 440x4"; - pcc->init_proc = init_proc_440x4; - pcc->check_pow = check_pow_nocheck; - pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | - PPC_DCR | PPC_WRTEE | - PPC_CACHE | PPC_CACHE_ICBI | - PPC_CACHE_DCBZ | PPC_CACHE_DCBA | - PPC_MEM_TLBSYNC | PPC_MFTB | - PPC_BOOKE | PPC_4xx_COMMON | PPC_405_MAC | - PPC_440_SPEC; - pcc->msr_mask = (1ull << MSR_POW) | - (1ull << MSR_CE) | - (1ull << MSR_EE) | - (1ull << MSR_PR) | - (1ull << MSR_FP) | - (1ull << MSR_ME) | - (1ull << MSR_FE0) | - (1ull << MSR_DWE) | - (1ull << MSR_DE) | - (1ull << MSR_FE1) | - (1ull << MSR_IR) | - (1ull << MSR_DR); - pcc->mmu_model = POWERPC_MMU_BOOKE; - pcc->excp_model = POWERPC_EXCP_BOOKE; - pcc->bus_model = PPC_FLAGS_INPUT_BookE; - pcc->bfd_mach = bfd_mach_ppc_403; - pcc->flags = POWERPC_FLAG_CE | POWERPC_FLAG_DWE | - POWERPC_FLAG_DE | POWERPC_FLAG_BUS_CLK; -} - static void init_proc_440x5(CPUPPCState *env) { /* Time base */ From 180952cedc0eef37ac43f9de66bdc0ebd43e2ed8 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Wed, 9 Feb 2022 09:08:55 +0100 Subject: [PATCH 330/460] target/ppc: Introduce powerpc_excp_booke MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce a new powerpc_excp function specific for BookE CPUs. This commit copies powerpc_excp_legacy verbatim so the next one has a clean diff. Signed-off-by: Fabiano Rosas Message-Id: <20220128224018.1228062-2-farosas@linux.ibm.com> Signed-off-by: Cédric Le Goater --- target/ppc/excp_helper.c | 474 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 474 insertions(+) diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index c107953dec..1571ab6496 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -745,6 +745,477 @@ static void powerpc_excp_74xx(PowerPCCPU *cpu, int excp) powerpc_set_excp_state(cpu, vector, new_msr); } +static void powerpc_excp_booke(PowerPCCPU *cpu, int excp) +{ + CPUState *cs = CPU(cpu); + CPUPPCState *env = &cpu->env; + int excp_model = env->excp_model; + target_ulong msr, new_msr, vector; + int srr0, srr1, lev = -1; + + if (excp <= POWERPC_EXCP_NONE || excp >= POWERPC_EXCP_NB) { + cpu_abort(cs, "Invalid PowerPC exception %d. Aborting\n", excp); + } + + qemu_log_mask(CPU_LOG_INT, "Raise exception at " TARGET_FMT_lx + " => %s (%d) error=%02x\n", env->nip, powerpc_excp_name(excp), + excp, env->error_code); + + /* new srr1 value excluding must-be-zero bits */ + if (excp_model == POWERPC_EXCP_BOOKE) { + msr = env->msr; + } else { + msr = env->msr & ~0x783f0000ULL; + } + + /* + * new interrupt handler msr preserves existing HV and ME unless + * explicitly overriden + */ + new_msr = env->msr & (((target_ulong)1 << MSR_ME) | MSR_HVB); + + /* target registers */ + srr0 = SPR_SRR0; + srr1 = SPR_SRR1; + + /* + * check for special resume at 0x100 from doze/nap/sleep/winkle on + * P7/P8/P9 + */ + if (env->resume_as_sreset) { + excp = powerpc_reset_wakeup(cs, env, excp, &msr); + } + + /* + * Hypervisor emulation assistance interrupt only exists on server + * arch 2.05 server or later. We also don't want to generate it if + * we don't have HVB in msr_mask (PAPR mode). + */ + if (excp == POWERPC_EXCP_HV_EMU +#if defined(TARGET_PPC64) + && !(mmu_is_64bit(env->mmu_model) && (env->msr_mask & MSR_HVB)) +#endif /* defined(TARGET_PPC64) */ + + ) { + excp = POWERPC_EXCP_PROGRAM; + } + +#ifdef TARGET_PPC64 + /* + * SPEU and VPU share the same IVOR but they exist in different + * processors. SPEU is e500v1/2 only and VPU is e6500 only. + */ + if (excp_model == POWERPC_EXCP_BOOKE && excp == POWERPC_EXCP_VPU) { + excp = POWERPC_EXCP_SPEU; + } +#endif + + vector = env->excp_vectors[excp]; + if (vector == (target_ulong)-1ULL) { + cpu_abort(cs, "Raised an exception without defined vector %d\n", + excp); + } + + vector |= env->excp_prefix; + + switch (excp) { + case POWERPC_EXCP_CRITICAL: /* Critical input */ + switch (excp_model) { + case POWERPC_EXCP_40x: + srr0 = SPR_40x_SRR2; + srr1 = SPR_40x_SRR3; + break; + case POWERPC_EXCP_BOOKE: + srr0 = SPR_BOOKE_CSRR0; + srr1 = SPR_BOOKE_CSRR1; + break; + case POWERPC_EXCP_G2: + break; + default: + goto excp_invalid; + } + break; + case POWERPC_EXCP_MCHECK: /* Machine check exception */ + if (msr_me == 0) { + /* + * 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"); + } + cs->halted = 1; + cpu_interrupt_exittb(cs); + } + if (env->msr_mask & MSR_HVB) { + /* + * ISA specifies HV, but can be delivered to guest with HV + * clear (e.g., see FWNMI in PAPR). + */ + new_msr |= (target_ulong)MSR_HVB; + } + + /* machine check exceptions don't have ME set */ + new_msr &= ~((target_ulong)1 << MSR_ME); + + /* XXX: should also have something loaded in DAR / DSISR */ + switch (excp_model) { + case POWERPC_EXCP_40x: + srr0 = SPR_40x_SRR2; + srr1 = SPR_40x_SRR3; + break; + case POWERPC_EXCP_BOOKE: + /* FIXME: choose one or the other based on CPU type */ + srr0 = SPR_BOOKE_MCSRR0; + srr1 = SPR_BOOKE_MCSRR1; + + env->spr[SPR_BOOKE_CSRR0] = env->nip; + env->spr[SPR_BOOKE_CSRR1] = msr; + break; + default: + break; + } + break; + case POWERPC_EXCP_DSI: /* Data storage exception */ + trace_ppc_excp_dsi(env->spr[SPR_DSISR], env->spr[SPR_DAR]); + break; + case POWERPC_EXCP_ISI: /* Instruction storage exception */ + trace_ppc_excp_isi(msr, env->nip); + msr |= env->error_code; + break; + case POWERPC_EXCP_EXTERNAL: /* External input */ + { + bool lpes0; + + cs = CPU(cpu); + + /* + * Exception targeting modifiers + * + * LPES0 is supported on POWER7/8/9 + * LPES1 is not supported (old iSeries mode) + * + * On anything else, we behave as if LPES0 is 1 + * (externals don't alter MSR:HV) + */ +#if defined(TARGET_PPC64) + if (excp_model == POWERPC_EXCP_POWER7 || + excp_model == POWERPC_EXCP_POWER8 || + excp_model == POWERPC_EXCP_POWER9 || + excp_model == POWERPC_EXCP_POWER10) { + lpes0 = !!(env->spr[SPR_LPCR] & LPCR_LPES0); + } else +#endif /* defined(TARGET_PPC64) */ + { + lpes0 = true; + } + + if (!lpes0) { + new_msr |= (target_ulong)MSR_HVB; + new_msr |= env->msr & ((target_ulong)1 << MSR_RI); + srr0 = SPR_HSRR0; + srr1 = SPR_HSRR1; + } + if (env->mpic_proxy) { + /* IACK the IRQ on delivery */ + env->spr[SPR_BOOKE_EPR] = ldl_phys(cs->as, env->mpic_iack); + } + break; + } + case POWERPC_EXCP_ALIGN: /* Alignment exception */ + /* Get rS/rD and rA from faulting opcode */ + /* + * Note: the opcode fields will not be set properly for a + * direct store load/store, but nobody cares as nobody + * actually uses direct store segments. + */ + env->spr[SPR_DSISR] |= (env->error_code & 0x03FF0000) >> 16; + break; + case POWERPC_EXCP_PROGRAM: /* Program exception */ + switch (env->error_code & ~0xF) { + case POWERPC_EXCP_FP: + if ((msr_fe0 == 0 && msr_fe1 == 0) || msr_fp == 0) { + trace_ppc_excp_fp_ignore(); + cs->exception_index = POWERPC_EXCP_NONE; + env->error_code = 0; + return; + } + + /* + * FP exceptions always have NIP pointing to the faulting + * instruction, so always use store_next and claim we are + * precise in the MSR. + */ + msr |= 0x00100000; + env->spr[SPR_BOOKE_ESR] = ESR_FP; + break; + case POWERPC_EXCP_INVAL: + trace_ppc_excp_inval(env->nip); + msr |= 0x00080000; + env->spr[SPR_BOOKE_ESR] = ESR_PIL; + break; + case POWERPC_EXCP_PRIV: + msr |= 0x00040000; + env->spr[SPR_BOOKE_ESR] = ESR_PPR; + break; + case POWERPC_EXCP_TRAP: + msr |= 0x00020000; + env->spr[SPR_BOOKE_ESR] = ESR_PTR; + break; + default: + /* Should never occur */ + cpu_abort(cs, "Invalid program exception %d. Aborting\n", + env->error_code); + break; + } + break; + case POWERPC_EXCP_SYSCALL: /* System call exception */ + lev = env->error_code; + + if ((lev == 1) && cpu->vhyp) { + dump_hcall(env); + } else { + dump_syscall(env); + } + + /* + * We need to correct the NIP which in this case is supposed + * to point to the next instruction + */ + env->nip += 4; + + /* "PAPR mode" built-in hypercall emulation */ + if ((lev == 1) && cpu->vhyp) { + PPCVirtualHypervisorClass *vhc = + PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp); + vhc->hypercall(cpu->vhyp, cpu); + return; + } + if (lev == 1) { + new_msr |= (target_ulong)MSR_HVB; + } + break; + case POWERPC_EXCP_SYSCALL_VECTORED: /* scv exception */ + lev = env->error_code; + dump_syscall(env); + env->nip += 4; + new_msr |= env->msr & ((target_ulong)1 << MSR_EE); + new_msr |= env->msr & ((target_ulong)1 << MSR_RI); + + vector += lev * 0x20; + + env->lr = env->nip; + env->ctr = msr; + break; + case POWERPC_EXCP_FPU: /* Floating-point unavailable exception */ + case POWERPC_EXCP_APU: /* Auxiliary processor unavailable */ + case POWERPC_EXCP_DECR: /* Decrementer exception */ + break; + case POWERPC_EXCP_FIT: /* Fixed-interval timer interrupt */ + /* FIT on 4xx */ + trace_ppc_excp_print("FIT"); + break; + case POWERPC_EXCP_WDT: /* Watchdog timer interrupt */ + trace_ppc_excp_print("WDT"); + switch (excp_model) { + case POWERPC_EXCP_BOOKE: + srr0 = SPR_BOOKE_CSRR0; + srr1 = SPR_BOOKE_CSRR1; + break; + default: + break; + } + break; + case POWERPC_EXCP_DTLB: /* Data TLB error */ + case POWERPC_EXCP_ITLB: /* Instruction TLB error */ + break; + case POWERPC_EXCP_DEBUG: /* Debug interrupt */ + if (env->flags & POWERPC_FLAG_DE) { + /* FIXME: choose one or the other based on CPU type */ + srr0 = SPR_BOOKE_DSRR0; + srr1 = SPR_BOOKE_DSRR1; + + env->spr[SPR_BOOKE_CSRR0] = env->nip; + env->spr[SPR_BOOKE_CSRR1] = msr; + + /* DBSR already modified by caller */ + } else { + cpu_abort(cs, "Debug exception triggered on unsupported model\n"); + } + break; + case POWERPC_EXCP_SPEU: /* SPE/embedded floating-point unavailable/VPU */ + env->spr[SPR_BOOKE_ESR] = ESR_SPV; + break; + case POWERPC_EXCP_DOORI: /* Embedded doorbell interrupt */ + break; + case POWERPC_EXCP_DOORCI: /* Embedded doorbell critical interrupt */ + srr0 = SPR_BOOKE_CSRR0; + srr1 = SPR_BOOKE_CSRR1; + break; + case POWERPC_EXCP_RESET: /* System reset exception */ + /* A power-saving exception sets ME, otherwise it is unchanged */ + if (msr_pow) { + /* indicate that we resumed from power save mode */ + msr |= 0x10000; + new_msr |= ((target_ulong)1 << MSR_ME); + } + if (env->msr_mask & MSR_HVB) { + /* + * ISA specifies HV, but can be delivered to guest with HV + * clear (e.g., see FWNMI in PAPR, NMI injection in QEMU). + */ + new_msr |= (target_ulong)MSR_HVB; + } else { + if (msr_pow) { + cpu_abort(cs, "Trying to deliver power-saving system reset " + "exception %d with no HV support\n", excp); + } + } + break; + case POWERPC_EXCP_DSEG: /* Data segment exception */ + case POWERPC_EXCP_ISEG: /* Instruction segment exception */ + case POWERPC_EXCP_TRACE: /* Trace exception */ + break; + case POWERPC_EXCP_HISI: /* Hypervisor instruction storage exception */ + msr |= env->error_code; + /* fall through */ + case POWERPC_EXCP_HDECR: /* Hypervisor decrementer exception */ + case POWERPC_EXCP_HDSI: /* Hypervisor data storage exception */ + case POWERPC_EXCP_HDSEG: /* Hypervisor data segment exception */ + case POWERPC_EXCP_HISEG: /* Hypervisor instruction segment exception */ + case POWERPC_EXCP_SDOOR_HV: /* Hypervisor Doorbell interrupt */ + case POWERPC_EXCP_HV_EMU: + case POWERPC_EXCP_HVIRT: /* Hypervisor virtualization */ + srr0 = SPR_HSRR0; + srr1 = SPR_HSRR1; + new_msr |= (target_ulong)MSR_HVB; + new_msr |= env->msr & ((target_ulong)1 << MSR_RI); + break; + case POWERPC_EXCP_VPU: /* Vector unavailable exception */ + case POWERPC_EXCP_VSXU: /* VSX unavailable exception */ + case POWERPC_EXCP_FU: /* Facility unavailable exception */ +#ifdef TARGET_PPC64 + env->spr[SPR_FSCR] |= ((target_ulong)env->error_code << 56); +#endif + break; + case POWERPC_EXCP_HV_FU: /* Hypervisor Facility Unavailable Exception */ +#ifdef TARGET_PPC64 + env->spr[SPR_HFSCR] |= ((target_ulong)env->error_code << FSCR_IC_POS); + srr0 = SPR_HSRR0; + srr1 = SPR_HSRR1; + new_msr |= (target_ulong)MSR_HVB; + new_msr |= env->msr & ((target_ulong)1 << MSR_RI); +#endif + break; + case POWERPC_EXCP_PIT: /* Programmable interval timer interrupt */ + trace_ppc_excp_print("PIT"); + break; + case POWERPC_EXCP_IFTLB: /* Instruction fetch TLB error */ + case POWERPC_EXCP_DLTLB: /* Data load TLB miss */ + case POWERPC_EXCP_DSTLB: /* Data store TLB miss */ + switch (excp_model) { + case POWERPC_EXCP_602: + case POWERPC_EXCP_603: + case POWERPC_EXCP_G2: + /* Swap temporary saved registers with GPRs */ + if (!(new_msr & ((target_ulong)1 << MSR_TGPR))) { + new_msr |= (target_ulong)1 << MSR_TGPR; + hreg_swap_gpr_tgpr(env); + } + /* fall through */ + case POWERPC_EXCP_7x5: + ppc_excp_debug_sw_tlb(env, excp); + + msr |= env->crf[0] << 28; + msr |= env->error_code; /* key, D/I, S/L bits */ + /* Set way using a LRU mechanism */ + msr |= ((env->last_way + 1) & (env->nb_ways - 1)) << 17; + break; + default: + cpu_abort(cs, "Invalid TLB miss exception\n"); + break; + } + break; + case POWERPC_EXCP_EFPDI: /* Embedded floating-point data interrupt */ + case POWERPC_EXCP_EFPRI: /* Embedded floating-point round interrupt */ + case POWERPC_EXCP_EPERFM: /* Embedded performance monitor interrupt */ + case POWERPC_EXCP_IO: /* IO error exception */ + case POWERPC_EXCP_RUNM: /* Run mode exception */ + case POWERPC_EXCP_EMUL: /* Emulation trap exception */ + case POWERPC_EXCP_FPA: /* Floating-point assist exception */ + case POWERPC_EXCP_DABR: /* Data address breakpoint */ + case POWERPC_EXCP_IABR: /* Instruction address breakpoint */ + case POWERPC_EXCP_SMI: /* System management interrupt */ + case POWERPC_EXCP_THERM: /* Thermal interrupt */ + case POWERPC_EXCP_PERFM: /* Embedded performance monitor interrupt */ + case POWERPC_EXCP_VPUA: /* Vector assist exception */ + case POWERPC_EXCP_SOFTP: /* Soft patch exception */ + case POWERPC_EXCP_MAINT: /* Maintenance exception */ + case POWERPC_EXCP_MEXTBR: /* Maskable external breakpoint */ + case POWERPC_EXCP_NMEXTBR: /* Non maskable external breakpoint */ + cpu_abort(cs, "%s exception not implemented\n", + powerpc_excp_name(excp)); + break; + default: + excp_invalid: + cpu_abort(cs, "Invalid PowerPC exception %d. Aborting\n", excp); + break; + } + + /* Sanity check */ + if (!(env->msr_mask & MSR_HVB)) { + if (new_msr & MSR_HVB) { + cpu_abort(cs, "Trying to deliver HV exception (MSR) %d with " + "no HV support\n", excp); + } + if (srr0 == SPR_HSRR0) { + cpu_abort(cs, "Trying to deliver HV exception (HSRR) %d with " + "no HV support\n", excp); + } + } + + /* + * Sort out endianness of interrupt, this differs depending on the + * CPU, the HV mode, etc... + */ + if (ppc_interrupts_little_endian(cpu, !!(new_msr & MSR_HVB))) { + new_msr |= (target_ulong)1 << MSR_LE; + } + +#if defined(TARGET_PPC64) + if (excp_model == POWERPC_EXCP_BOOKE) { + if (env->spr[SPR_BOOKE_EPCR] & EPCR_ICM) { + /* Cat.64-bit: EPCR.ICM is copied to MSR.CM */ + new_msr |= (target_ulong)1 << MSR_CM; + } else { + vector = (uint32_t)vector; + } + } else { + if (!msr_isf && !mmu_is_64bit(env->mmu_model)) { + vector = (uint32_t)vector; + } else { + new_msr |= (target_ulong)1 << MSR_SF; + } + } +#endif + + if (excp != POWERPC_EXCP_SYSCALL_VECTORED) { + /* Save PC */ + env->spr[srr0] = env->nip; + + /* Save MSR */ + env->spr[srr1] = msr; + } + + /* This can update new_msr and vector if AIL applies */ + ppc_excp_apply_ail(cpu, excp_model, excp, msr, &new_msr, &vector); + + powerpc_set_excp_state(cpu, vector, new_msr); +} + #ifdef TARGET_PPC64 static void powerpc_excp_books(PowerPCCPU *cpu, int excp) { @@ -1531,6 +2002,9 @@ static void powerpc_excp(PowerPCCPU *cpu, int excp) case POWERPC_EXCP_74xx: powerpc_excp_74xx(cpu, excp); break; + case POWERPC_EXCP_BOOKE: + powerpc_excp_booke(cpu, excp); + break; case POWERPC_EXCP_970: case POWERPC_EXCP_POWER7: case POWERPC_EXCP_POWER8: From 9dc20cc37db9d13ccce00e7274f22d41f5306443 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Wed, 9 Feb 2022 09:08:55 +0100 Subject: [PATCH 331/460] target/ppc: Simplify powerpc_excp_booke MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Differences from the generic powerpc_excp code: - No MSR bits are cleared at interrupt dispatch; - No MSR_HV; - No power saving states; - No Hypervisor Emulation Assistance; - SPEU needs special handling; - Big endian only; - Both 64 and 32 bits; - No System call vectored; - No Alternate Interrupt Location. Exceptions used: POWERPC_EXCP_ALIGN POWERPC_EXCP_APU POWERPC_EXCP_CRITICAL POWERPC_EXCP_DEBUG POWERPC_EXCP_DECR POWERPC_EXCP_DSI POWERPC_EXCP_DTLB POWERPC_EXCP_EFPDI POWERPC_EXCP_EFPRI POWERPC_EXCP_EXTERNAL POWERPC_EXCP_FIT POWERPC_EXCP_FPU POWERPC_EXCP_ISI POWERPC_EXCP_ITLB POWERPC_EXCP_MCHECK POWERPC_EXCP_PROGRAM POWERPC_EXCP_RESET POWERPC_EXCP_SPEU POWERPC_EXCP_SYSCALL POWERPC_EXCP_WDT Signed-off-by: Fabiano Rosas Message-Id: <20220128224018.1228062-3-farosas@linux.ibm.com> Signed-off-by: Cédric Le Goater --- target/ppc/excp_helper.c | 165 ++++----------------------------------- 1 file changed, 14 insertions(+), 151 deletions(-) diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index 1571ab6496..596c16a678 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -761,42 +761,23 @@ static void powerpc_excp_booke(PowerPCCPU *cpu, int excp) " => %s (%d) error=%02x\n", env->nip, powerpc_excp_name(excp), excp, env->error_code); - /* new srr1 value excluding must-be-zero bits */ - if (excp_model == POWERPC_EXCP_BOOKE) { - msr = env->msr; - } else { - msr = env->msr & ~0x783f0000ULL; - } + msr = env->msr; /* - * new interrupt handler msr preserves existing HV and ME unless + * new interrupt handler msr preserves existing ME unless * explicitly overriden */ - new_msr = env->msr & (((target_ulong)1 << MSR_ME) | MSR_HVB); + new_msr = env->msr & ((target_ulong)1 << MSR_ME); /* target registers */ srr0 = SPR_SRR0; srr1 = SPR_SRR1; - /* - * check for special resume at 0x100 from doze/nap/sleep/winkle on - * P7/P8/P9 - */ - if (env->resume_as_sreset) { - excp = powerpc_reset_wakeup(cs, env, excp, &msr); - } - /* * Hypervisor emulation assistance interrupt only exists on server - * arch 2.05 server or later. We also don't want to generate it if - * we don't have HVB in msr_mask (PAPR mode). + * arch 2.05 server or later. */ - if (excp == POWERPC_EXCP_HV_EMU -#if defined(TARGET_PPC64) - && !(mmu_is_64bit(env->mmu_model) && (env->msr_mask & MSR_HVB)) -#endif /* defined(TARGET_PPC64) */ - - ) { + if (excp == POWERPC_EXCP_HV_EMU) { excp = POWERPC_EXCP_PROGRAM; } @@ -805,7 +786,7 @@ static void powerpc_excp_booke(PowerPCCPU *cpu, int excp) * SPEU and VPU share the same IVOR but they exist in different * processors. SPEU is e500v1/2 only and VPU is e6500 only. */ - if (excp_model == POWERPC_EXCP_BOOKE && excp == POWERPC_EXCP_VPU) { + if (excp == POWERPC_EXCP_VPU) { excp = POWERPC_EXCP_SPEU; } #endif @@ -998,18 +979,6 @@ static void powerpc_excp_booke(PowerPCCPU *cpu, int excp) new_msr |= (target_ulong)MSR_HVB; } break; - case POWERPC_EXCP_SYSCALL_VECTORED: /* scv exception */ - lev = env->error_code; - dump_syscall(env); - env->nip += 4; - new_msr |= env->msr & ((target_ulong)1 << MSR_EE); - new_msr |= env->msr & ((target_ulong)1 << MSR_RI); - - vector += lev * 0x20; - - env->lr = env->nip; - env->ctr = msr; - break; case POWERPC_EXCP_FPU: /* Floating-point unavailable exception */ case POWERPC_EXCP_APU: /* Auxiliary processor unavailable */ case POWERPC_EXCP_DECR: /* Decrementer exception */ @@ -1049,12 +1018,6 @@ static void powerpc_excp_booke(PowerPCCPU *cpu, int excp) case POWERPC_EXCP_SPEU: /* SPE/embedded floating-point unavailable/VPU */ env->spr[SPR_BOOKE_ESR] = ESR_SPV; break; - case POWERPC_EXCP_DOORI: /* Embedded doorbell interrupt */ - break; - case POWERPC_EXCP_DOORCI: /* Embedded doorbell critical interrupt */ - srr0 = SPR_BOOKE_CSRR0; - srr1 = SPR_BOOKE_CSRR1; - break; case POWERPC_EXCP_RESET: /* System reset exception */ /* A power-saving exception sets ME, otherwise it is unchanged */ if (msr_pow) { @@ -1075,87 +1038,8 @@ static void powerpc_excp_booke(PowerPCCPU *cpu, int excp) } } break; - case POWERPC_EXCP_DSEG: /* Data segment exception */ - case POWERPC_EXCP_ISEG: /* Instruction segment exception */ - case POWERPC_EXCP_TRACE: /* Trace exception */ - break; - case POWERPC_EXCP_HISI: /* Hypervisor instruction storage exception */ - msr |= env->error_code; - /* fall through */ - case POWERPC_EXCP_HDECR: /* Hypervisor decrementer exception */ - case POWERPC_EXCP_HDSI: /* Hypervisor data storage exception */ - case POWERPC_EXCP_HDSEG: /* Hypervisor data segment exception */ - case POWERPC_EXCP_HISEG: /* Hypervisor instruction segment exception */ - case POWERPC_EXCP_SDOOR_HV: /* Hypervisor Doorbell interrupt */ - case POWERPC_EXCP_HV_EMU: - case POWERPC_EXCP_HVIRT: /* Hypervisor virtualization */ - srr0 = SPR_HSRR0; - srr1 = SPR_HSRR1; - new_msr |= (target_ulong)MSR_HVB; - new_msr |= env->msr & ((target_ulong)1 << MSR_RI); - break; - case POWERPC_EXCP_VPU: /* Vector unavailable exception */ - case POWERPC_EXCP_VSXU: /* VSX unavailable exception */ - case POWERPC_EXCP_FU: /* Facility unavailable exception */ -#ifdef TARGET_PPC64 - env->spr[SPR_FSCR] |= ((target_ulong)env->error_code << 56); -#endif - break; - case POWERPC_EXCP_HV_FU: /* Hypervisor Facility Unavailable Exception */ -#ifdef TARGET_PPC64 - env->spr[SPR_HFSCR] |= ((target_ulong)env->error_code << FSCR_IC_POS); - srr0 = SPR_HSRR0; - srr1 = SPR_HSRR1; - new_msr |= (target_ulong)MSR_HVB; - new_msr |= env->msr & ((target_ulong)1 << MSR_RI); -#endif - break; - case POWERPC_EXCP_PIT: /* Programmable interval timer interrupt */ - trace_ppc_excp_print("PIT"); - break; - case POWERPC_EXCP_IFTLB: /* Instruction fetch TLB error */ - case POWERPC_EXCP_DLTLB: /* Data load TLB miss */ - case POWERPC_EXCP_DSTLB: /* Data store TLB miss */ - switch (excp_model) { - case POWERPC_EXCP_602: - case POWERPC_EXCP_603: - case POWERPC_EXCP_G2: - /* Swap temporary saved registers with GPRs */ - if (!(new_msr & ((target_ulong)1 << MSR_TGPR))) { - new_msr |= (target_ulong)1 << MSR_TGPR; - hreg_swap_gpr_tgpr(env); - } - /* fall through */ - case POWERPC_EXCP_7x5: - ppc_excp_debug_sw_tlb(env, excp); - - msr |= env->crf[0] << 28; - msr |= env->error_code; /* key, D/I, S/L bits */ - /* Set way using a LRU mechanism */ - msr |= ((env->last_way + 1) & (env->nb_ways - 1)) << 17; - break; - default: - cpu_abort(cs, "Invalid TLB miss exception\n"); - break; - } - break; case POWERPC_EXCP_EFPDI: /* Embedded floating-point data interrupt */ case POWERPC_EXCP_EFPRI: /* Embedded floating-point round interrupt */ - case POWERPC_EXCP_EPERFM: /* Embedded performance monitor interrupt */ - case POWERPC_EXCP_IO: /* IO error exception */ - case POWERPC_EXCP_RUNM: /* Run mode exception */ - case POWERPC_EXCP_EMUL: /* Emulation trap exception */ - case POWERPC_EXCP_FPA: /* Floating-point assist exception */ - case POWERPC_EXCP_DABR: /* Data address breakpoint */ - case POWERPC_EXCP_IABR: /* Instruction address breakpoint */ - case POWERPC_EXCP_SMI: /* System management interrupt */ - case POWERPC_EXCP_THERM: /* Thermal interrupt */ - case POWERPC_EXCP_PERFM: /* Embedded performance monitor interrupt */ - case POWERPC_EXCP_VPUA: /* Vector assist exception */ - case POWERPC_EXCP_SOFTP: /* Soft patch exception */ - case POWERPC_EXCP_MAINT: /* Maintenance exception */ - case POWERPC_EXCP_MEXTBR: /* Maskable external breakpoint */ - case POWERPC_EXCP_NMEXTBR: /* Non maskable external breakpoint */ cpu_abort(cs, "%s exception not implemented\n", powerpc_excp_name(excp)); break; @@ -1177,41 +1061,20 @@ static void powerpc_excp_booke(PowerPCCPU *cpu, int excp) } } - /* - * Sort out endianness of interrupt, this differs depending on the - * CPU, the HV mode, etc... - */ - if (ppc_interrupts_little_endian(cpu, !!(new_msr & MSR_HVB))) { - new_msr |= (target_ulong)1 << MSR_LE; - } - #if defined(TARGET_PPC64) - if (excp_model == POWERPC_EXCP_BOOKE) { - if (env->spr[SPR_BOOKE_EPCR] & EPCR_ICM) { - /* Cat.64-bit: EPCR.ICM is copied to MSR.CM */ - new_msr |= (target_ulong)1 << MSR_CM; - } else { - vector = (uint32_t)vector; - } + if (env->spr[SPR_BOOKE_EPCR] & EPCR_ICM) { + /* Cat.64-bit: EPCR.ICM is copied to MSR.CM */ + new_msr |= (target_ulong)1 << MSR_CM; } else { - if (!msr_isf && !mmu_is_64bit(env->mmu_model)) { - vector = (uint32_t)vector; - } else { - new_msr |= (target_ulong)1 << MSR_SF; - } + vector = (uint32_t)vector; } #endif - if (excp != POWERPC_EXCP_SYSCALL_VECTORED) { - /* Save PC */ - env->spr[srr0] = env->nip; + /* Save PC */ + env->spr[srr0] = env->nip; - /* Save MSR */ - env->spr[srr1] = msr; - } - - /* This can update new_msr and vector if AIL applies */ - ppc_excp_apply_ail(cpu, excp_model, excp, msr, &new_msr, &vector); + /* Save MSR */ + env->spr[srr1] = msr; powerpc_set_excp_state(cpu, vector, new_msr); } From 9c9b67fe91a0e5ff3961d85d7d51dce4075a6f08 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Wed, 9 Feb 2022 09:08:55 +0100 Subject: [PATCH 332/460] target/ppc: booke: Critical exception cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove 40x and G2 code. Signed-off-by: Fabiano Rosas Message-Id: <20220128224018.1228062-4-farosas@linux.ibm.com> Signed-off-by: Cédric Le Goater --- target/ppc/excp_helper.c | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index 596c16a678..8a656ace6f 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -801,20 +801,8 @@ static void powerpc_excp_booke(PowerPCCPU *cpu, int excp) switch (excp) { case POWERPC_EXCP_CRITICAL: /* Critical input */ - switch (excp_model) { - case POWERPC_EXCP_40x: - srr0 = SPR_40x_SRR2; - srr1 = SPR_40x_SRR3; - break; - case POWERPC_EXCP_BOOKE: - srr0 = SPR_BOOKE_CSRR0; - srr1 = SPR_BOOKE_CSRR1; - break; - case POWERPC_EXCP_G2: - break; - default: - goto excp_invalid; - } + srr0 = SPR_BOOKE_CSRR0; + srr1 = SPR_BOOKE_CSRR1; break; case POWERPC_EXCP_MCHECK: /* Machine check exception */ if (msr_me == 0) { @@ -1044,7 +1032,6 @@ static void powerpc_excp_booke(PowerPCCPU *cpu, int excp) powerpc_excp_name(excp)); break; default: - excp_invalid: cpu_abort(cs, "Invalid PowerPC exception %d. Aborting\n", excp); break; } From db403211f8048c08cc948de8882c1a8c99f021dd Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Wed, 9 Feb 2022 09:08:55 +0100 Subject: [PATCH 333/460] target/ppc: booke: Machine Check cleanups MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There's no MSR_HV in BookE. Also remove 40x code. Signed-off-by: Fabiano Rosas Message-Id: <20220128224018.1228062-5-farosas@linux.ibm.com> Signed-off-by: Cédric Le Goater --- target/ppc/excp_helper.c | 29 ++++++----------------------- 1 file changed, 6 insertions(+), 23 deletions(-) diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index 8a656ace6f..4753b81527 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -819,34 +819,17 @@ static void powerpc_excp_booke(PowerPCCPU *cpu, int excp) cs->halted = 1; cpu_interrupt_exittb(cs); } - if (env->msr_mask & MSR_HVB) { - /* - * ISA specifies HV, but can be delivered to guest with HV - * clear (e.g., see FWNMI in PAPR). - */ - new_msr |= (target_ulong)MSR_HVB; - } /* machine check exceptions don't have ME set */ new_msr &= ~((target_ulong)1 << MSR_ME); - /* XXX: should also have something loaded in DAR / DSISR */ - switch (excp_model) { - case POWERPC_EXCP_40x: - srr0 = SPR_40x_SRR2; - srr1 = SPR_40x_SRR3; - break; - case POWERPC_EXCP_BOOKE: - /* FIXME: choose one or the other based on CPU type */ - srr0 = SPR_BOOKE_MCSRR0; - srr1 = SPR_BOOKE_MCSRR1; + /* FIXME: choose one or the other based on CPU type */ + srr0 = SPR_BOOKE_MCSRR0; + srr1 = SPR_BOOKE_MCSRR1; + + env->spr[SPR_BOOKE_CSRR0] = env->nip; + env->spr[SPR_BOOKE_CSRR1] = msr; - env->spr[SPR_BOOKE_CSRR0] = env->nip; - env->spr[SPR_BOOKE_CSRR1] = msr; - break; - default: - break; - } break; case POWERPC_EXCP_DSI: /* Data storage exception */ trace_ppc_excp_dsi(env->spr[SPR_DSISR], env->spr[SPR_DAR]); From afdbc869412637190a7d1e11c1b830ceebb6ffda Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Wed, 9 Feb 2022 09:08:55 +0100 Subject: [PATCH 334/460] target/ppc: booke: Data Storage exception cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is no DSISR or DAR in BookE. Change to ESR and DEAR. Signed-off-by: Fabiano Rosas Message-Id: <20220128224018.1228062-6-farosas@linux.ibm.com> Signed-off-by: Cédric Le Goater --- 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 4753b81527..c8bd78d5cb 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -832,7 +832,7 @@ static void powerpc_excp_booke(PowerPCCPU *cpu, int excp) break; case POWERPC_EXCP_DSI: /* Data storage exception */ - trace_ppc_excp_dsi(env->spr[SPR_DSISR], env->spr[SPR_DAR]); + trace_ppc_excp_dsi(env->spr[SPR_BOOKE_ESR], env->spr[SPR_BOOKE_DEAR]); break; case POWERPC_EXCP_ISI: /* Instruction storage exception */ trace_ppc_excp_isi(msr, env->nip); From b583351d4b149980c07d7f7c26acc937d514783d Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Wed, 9 Feb 2022 09:08:55 +0100 Subject: [PATCH 335/460] target/ppc: booke: Instruction storage exception cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The SRR1 should be set to the MSR value. There are no diagnostic bits in the SRR1 for BookE. Note that this fixes a bug where MSR_GS would be set and Linux would go into KVM code when there's no KVM guest. Signed-off-by: Fabiano Rosas Message-Id: <20220128224018.1228062-7-farosas@linux.ibm.com> Signed-off-by: Cédric Le Goater --- target/ppc/excp_helper.c | 1 - 1 file changed, 1 deletion(-) diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index c8bd78d5cb..8340146902 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -836,7 +836,6 @@ static void powerpc_excp_booke(PowerPCCPU *cpu, int excp) break; case POWERPC_EXCP_ISI: /* Instruction storage exception */ trace_ppc_excp_isi(msr, env->nip); - msr |= env->error_code; break; case POWERPC_EXCP_EXTERNAL: /* External input */ { From 5d54e8c18e56568fc52143dab0316e18015af185 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Wed, 9 Feb 2022 09:08:55 +0100 Subject: [PATCH 336/460] target/ppc: booke: External interrupt cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is no LPES0 in BookE and no MSR_HV. Signed-off-by: Fabiano Rosas Message-Id: <20220128224018.1228062-8-farosas@linux.ibm.com> Signed-off-by: Cédric Le Goater --- target/ppc/excp_helper.c | 33 --------------------------------- 1 file changed, 33 deletions(-) diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index 8340146902..6d86ae04eb 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -838,44 +838,11 @@ static void powerpc_excp_booke(PowerPCCPU *cpu, int excp) trace_ppc_excp_isi(msr, env->nip); break; case POWERPC_EXCP_EXTERNAL: /* External input */ - { - bool lpes0; - - cs = CPU(cpu); - - /* - * Exception targeting modifiers - * - * LPES0 is supported on POWER7/8/9 - * LPES1 is not supported (old iSeries mode) - * - * On anything else, we behave as if LPES0 is 1 - * (externals don't alter MSR:HV) - */ -#if defined(TARGET_PPC64) - if (excp_model == POWERPC_EXCP_POWER7 || - excp_model == POWERPC_EXCP_POWER8 || - excp_model == POWERPC_EXCP_POWER9 || - excp_model == POWERPC_EXCP_POWER10) { - lpes0 = !!(env->spr[SPR_LPCR] & LPCR_LPES0); - } else -#endif /* defined(TARGET_PPC64) */ - { - lpes0 = true; - } - - if (!lpes0) { - new_msr |= (target_ulong)MSR_HVB; - new_msr |= env->msr & ((target_ulong)1 << MSR_RI); - srr0 = SPR_HSRR0; - srr1 = SPR_HSRR1; - } if (env->mpic_proxy) { /* IACK the IRQ on delivery */ env->spr[SPR_BOOKE_EPR] = ldl_phys(cs->as, env->mpic_iack); } break; - } case POWERPC_EXCP_ALIGN: /* Alignment exception */ /* Get rS/rD and rA from faulting opcode */ /* From f7a28f711939fe873dee762aa7dfe9f4bb63be06 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Wed, 9 Feb 2022 09:08:55 +0100 Subject: [PATCH 337/460] target/ppc: booke: Alignment interrupt cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit BookE has no DSISR or DAR. The proper registers ESR and DEAR were already set at this point. Signed-off-by: Fabiano Rosas Message-Id: <20220128224018.1228062-9-farosas@linux.ibm.com> Signed-off-by: Cédric Le Goater --- target/ppc/excp_helper.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index 6d86ae04eb..dfcb9995b8 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -844,13 +844,6 @@ static void powerpc_excp_booke(PowerPCCPU *cpu, int excp) } break; case POWERPC_EXCP_ALIGN: /* Alignment exception */ - /* Get rS/rD and rA from faulting opcode */ - /* - * Note: the opcode fields will not be set properly for a - * direct store load/store, but nobody cares as nobody - * actually uses direct store segments. - */ - env->spr[SPR_DSISR] |= (env->error_code & 0x03FF0000) >> 16; break; case POWERPC_EXCP_PROGRAM: /* Program exception */ switch (env->error_code & ~0xF) { From 904e842865f01cb1cb16ace5ecf49233c4612ec7 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Wed, 9 Feb 2022 09:08:55 +0100 Subject: [PATCH 338/460] target/ppc: booke: System Call exception cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit QEMU does not support BookE as a hypervisor. Signed-off-by: Fabiano Rosas Message-Id: <20220128224018.1228062-10-farosas@linux.ibm.com> Signed-off-by: Cédric Le Goater --- target/ppc/excp_helper.c | 21 ++------------------- 1 file changed, 2 insertions(+), 19 deletions(-) diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index dfcb9995b8..a5134e360c 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -751,7 +751,7 @@ static void powerpc_excp_booke(PowerPCCPU *cpu, int excp) CPUPPCState *env = &cpu->env; int excp_model = env->excp_model; target_ulong msr, new_msr, vector; - int srr0, srr1, lev = -1; + int srr0, srr1; if (excp <= POWERPC_EXCP_NONE || excp >= POWERPC_EXCP_NB) { cpu_abort(cs, "Invalid PowerPC exception %d. Aborting\n", excp); @@ -884,30 +884,13 @@ static void powerpc_excp_booke(PowerPCCPU *cpu, int excp) } break; case POWERPC_EXCP_SYSCALL: /* System call exception */ - lev = env->error_code; - - if ((lev == 1) && cpu->vhyp) { - dump_hcall(env); - } else { - dump_syscall(env); - } + dump_syscall(env); /* * We need to correct the NIP which in this case is supposed * to point to the next instruction */ env->nip += 4; - - /* "PAPR mode" built-in hypercall emulation */ - if ((lev == 1) && cpu->vhyp) { - PPCVirtualHypervisorClass *vhc = - PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp); - vhc->hypercall(cpu->vhyp, cpu); - return; - } - if (lev == 1) { - new_msr |= (target_ulong)MSR_HVB; - } break; case POWERPC_EXCP_FPU: /* Floating-point unavailable exception */ case POWERPC_EXCP_APU: /* Auxiliary processor unavailable */ From f2ba48779c626389eba828890f7c0979744ea305 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Wed, 9 Feb 2022 09:08:55 +0100 Subject: [PATCH 339/460] target/ppc: booke: Watchdog Timer interrupt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove the switch as this function applies to BookE only. Signed-off-by: Fabiano Rosas Message-Id: <20220128224018.1228062-11-farosas@linux.ibm.com> Signed-off-by: Cédric Le Goater --- target/ppc/excp_helper.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index a5134e360c..7c228dac58 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -749,7 +749,6 @@ static void powerpc_excp_booke(PowerPCCPU *cpu, int excp) { CPUState *cs = CPU(cpu); CPUPPCState *env = &cpu->env; - int excp_model = env->excp_model; target_ulong msr, new_msr, vector; int srr0, srr1; @@ -902,14 +901,8 @@ static void powerpc_excp_booke(PowerPCCPU *cpu, int excp) break; case POWERPC_EXCP_WDT: /* Watchdog timer interrupt */ trace_ppc_excp_print("WDT"); - switch (excp_model) { - case POWERPC_EXCP_BOOKE: - srr0 = SPR_BOOKE_CSRR0; - srr1 = SPR_BOOKE_CSRR1; - break; - default: - break; - } + srr0 = SPR_BOOKE_CSRR0; + srr1 = SPR_BOOKE_CSRR1; break; case POWERPC_EXCP_DTLB: /* Data TLB error */ case POWERPC_EXCP_ITLB: /* Instruction TLB error */ From 0fdd000a41ea57526d03b46301318f881abddc92 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Wed, 9 Feb 2022 09:08:55 +0100 Subject: [PATCH 340/460] target/ppc: booke: System Reset exception cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is no MSR_HV in BookE, so remove all of the HV logic. Signed-off-by: Fabiano Rosas Message-Id: <20220128224018.1228062-12-farosas@linux.ibm.com> Signed-off-by: Cédric Le Goater --- target/ppc/excp_helper.c | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index 7c228dac58..7d7d0a08b5 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -925,23 +925,9 @@ static void powerpc_excp_booke(PowerPCCPU *cpu, int excp) env->spr[SPR_BOOKE_ESR] = ESR_SPV; break; case POWERPC_EXCP_RESET: /* System reset exception */ - /* A power-saving exception sets ME, otherwise it is unchanged */ if (msr_pow) { - /* indicate that we resumed from power save mode */ - msr |= 0x10000; - new_msr |= ((target_ulong)1 << MSR_ME); - } - if (env->msr_mask & MSR_HVB) { - /* - * ISA specifies HV, but can be delivered to guest with HV - * clear (e.g., see FWNMI in PAPR, NMI injection in QEMU). - */ - new_msr |= (target_ulong)MSR_HVB; - } else { - if (msr_pow) { - cpu_abort(cs, "Trying to deliver power-saving system reset " - "exception %d with no HV support\n", excp); - } + cpu_abort(cs, "Trying to deliver power-saving system reset " + "exception %d with no HV support\n", excp); } break; case POWERPC_EXCP_EFPDI: /* Embedded floating-point data interrupt */ From 36387ca51c72407ec9f0f047767fcf3c6380b7de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 9 Feb 2022 09:08:55 +0100 Subject: [PATCH 341/460] target/ppc: Fix radix logging MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ppc_radix64_partition_scoped_xlate() logs the host page protection bits variable but it is uninitialized. The value is set later on in ppc_radix64_check_prot(). Remove the output. Fixes: Coverity CID 1468942 Signed-off-by: Cédric Le Goater Reviewed-by: Greg Kurz Message-Id: <20220203142145.1301749-1-clg@kaod.org> Signed-off-by: Cédric Le Goater --- target/ppc/mmu-radix64.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/target/ppc/mmu-radix64.c b/target/ppc/mmu-radix64.c index 040c055bff..d4e16bd7db 100644 --- a/target/ppc/mmu-radix64.c +++ b/target/ppc/mmu-radix64.c @@ -327,13 +327,9 @@ static int ppc_radix64_partition_scoped_xlate(PowerPCCPU *cpu, uint64_t pte; qemu_log_mask(CPU_LOG_MMU, "%s for %s @0x%"VADDR_PRIx - " mmu_idx %u (prot %c%c%c) 0x%"HWADDR_PRIx"\n", + " mmu_idx %u 0x%"HWADDR_PRIx"\n", __func__, access_str(access_type), - eaddr, mmu_idx, - *h_prot & PAGE_READ ? 'r' : '-', - *h_prot & PAGE_WRITE ? 'w' : '-', - *h_prot & PAGE_EXEC ? 'x' : '-', - g_raddr); + eaddr, mmu_idx, g_raddr); *h_page_size = PRTBE_R_GET_RTS(pate.dw0); /* No valid pte or access denied due to protection */ From 005b69fdccd798dd8f0996d0f1c93ff5a4672180 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 9 Feb 2022 09:08:55 +0100 Subject: [PATCH 342/460] target/ppc: Remove PowerPC 601 CPUs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The PowerPC 601 processor is the first generation of processors to implement the PowerPC architecture. It was designed as a bridge processor and also could execute most of the instructions of the previous POWER architecture. It was found on the first Macs and IBM RS/6000 workstations. There is not much interest in keeping the CPU model of this POWER-PowerPC bridge processor. We have the 603 and 604 CPU models of the 60x family which implement the complete PowerPC instruction set. Cc: "Hervé Poussineau" Cc: Laurent Vivier Signed-off-by: Cédric Le Goater Reviewed-by: Fabiano Rosas Message-Id: <20220203142756.1302515-1-clg@kaod.org> Signed-off-by: Cédric Le Goater --- hw/ppc/ppc.c | 21 - hw/ppc/prep.c | 9 +- linux-user/ppc/cpu_loop.c | 16 - target/ppc/cpu-models.c | 8 - target/ppc/cpu-models.h | 3 - target/ppc/cpu-qom.h | 6 - target/ppc/cpu.h | 39 +- target/ppc/cpu_init.c | 214 +------- target/ppc/excp_helper.c | 11 - target/ppc/helper.h | 13 - target/ppc/helper_regs.c | 10 - target/ppc/int_helper.c | 66 --- target/ppc/machine.c | 5 +- target/ppc/misc_helper.c | 40 -- target/ppc/mmu-hash32.c | 48 +- target/ppc/mmu-hash32.h | 9 - target/ppc/mmu_common.c | 21 - target/ppc/mmu_helper.c | 84 --- target/ppc/spr_tcg.h | 8 - target/ppc/timebase_helper.c | 20 - target/ppc/translate.c | 842 ----------------------------- target/ppc/translate/fp-impl.c.inc | 179 ------ 22 files changed, 13 insertions(+), 1659 deletions(-) diff --git a/hw/ppc/ppc.c b/hw/ppc/ppc.c index 462c87dba8..ba7fa0f3b5 100644 --- a/hw/ppc/ppc.c +++ b/hw/ppc/ppc.c @@ -1083,27 +1083,6 @@ clk_setup_cb cpu_ppc_tb_init (CPUPPCState *env, uint32_t freq) return &cpu_ppc_set_tb_clk; } -/* Specific helpers for POWER & PowerPC 601 RTC */ -void cpu_ppc601_store_rtcu (CPUPPCState *env, uint32_t value) -{ - _cpu_ppc_store_tbu(env, value); -} - -uint32_t cpu_ppc601_load_rtcu (CPUPPCState *env) -{ - return _cpu_ppc_load_tbu(env); -} - -void cpu_ppc601_store_rtcl (CPUPPCState *env, uint32_t value) -{ - cpu_ppc_store_tbl(env, value & 0x3FFFFF80); -} - -uint32_t cpu_ppc601_load_rtcl (CPUPPCState *env) -{ - return cpu_ppc_load_tbl(env) & 0x3FFFFF80; -} - /*****************************************************************************/ /* PowerPC 40x timers */ diff --git a/hw/ppc/prep.c b/hw/ppc/prep.c index 25a2e86b42..bf622aa38f 100644 --- a/hw/ppc/prep.c +++ b/hw/ppc/prep.c @@ -255,13 +255,8 @@ static void ibm_40p_init(MachineState *machine) exit(1); } - if (env->flags & POWERPC_FLAG_RTC_CLK) { - /* POWER / PowerPC 601 RTC clock frequency is 7.8125 MHz */ - cpu_ppc_tb_init(env, 7812500UL); - } else { - /* Set time-base frequency to 100 Mhz */ - cpu_ppc_tb_init(env, 100UL * 1000UL * 1000UL); - } + /* Set time-base frequency to 100 Mhz */ + cpu_ppc_tb_init(env, 100UL * 1000UL * 1000UL); qemu_register_reset(ppc_prep_reset, cpu); /* PCI host */ diff --git a/linux-user/ppc/cpu_loop.c b/linux-user/ppc/cpu_loop.c index 46e6ffd6d3..38097b02ff 100644 --- a/linux-user/ppc/cpu_loop.c +++ b/linux-user/ppc/cpu_loop.c @@ -54,14 +54,6 @@ uint64_t cpu_ppc_load_vtb(CPUPPCState *env) return cpu_ppc_get_tb(env); } -uint32_t cpu_ppc601_load_rtcu(CPUPPCState *env) -__attribute__ (( alias ("cpu_ppc_load_tbu") )); - -uint32_t cpu_ppc601_load_rtcl(CPUPPCState *env) -{ - return cpu_ppc_load_tbl(env) & 0x3FFFFF80; -} - /* XXX: to be fixed */ int ppc_dcr_read (ppc_dcr_t *dcr_env, int dcrn, uint32_t *valp) { @@ -289,14 +281,6 @@ void cpu_loop(CPUPPCState *env) cpu_abort(cs, "Programmable interval timer interrupt " "while in user mode. Aborting\n"); break; - case POWERPC_EXCP_IO: /* IO error exception */ - cpu_abort(cs, "IO error exception while in user mode. " - "Aborting\n"); - break; - case POWERPC_EXCP_RUNM: /* Run mode exception */ - cpu_abort(cs, "Run mode exception while in user mode. " - "Aborting\n"); - break; case POWERPC_EXCP_EMUL: /* Emulation trap exception */ cpu_abort(cs, "Emulation trap exception not handled\n"); break; diff --git a/target/ppc/cpu-models.c b/target/ppc/cpu-models.c index a2c720cc4d..976be5e0d1 100644 --- a/target/ppc/cpu-models.c +++ b/target/ppc/cpu-models.c @@ -422,12 +422,6 @@ CPU_POWERPC_MPC8641D, POWERPC_SVR_8641D, e600) /* 32 bits "classic" PowerPC */ /* PowerPC 6xx family */ - POWERPC_DEF("601_v0", CPU_POWERPC_601_v0, 601, - "PowerPC 601v0") - POWERPC_DEF("601_v1", CPU_POWERPC_601_v1, 601, - "PowerPC 601v1") - POWERPC_DEF("601_v2", CPU_POWERPC_601_v2, 601v, - "PowerPC 601v2") POWERPC_DEF("603", CPU_POWERPC_603, 603, "PowerPC 603") POWERPC_DEF("603e_v1.1", CPU_POWERPC_603E_v11, 603E, @@ -859,8 +853,6 @@ PowerPCCPUAlias ppc_cpu_aliases[] = { { "mpc8555", "mpc8555_v11" }, { "mpc8555e", "mpc8555e_v11" }, { "mpc8560", "mpc8560_v21" }, - { "601", "601_v2" }, - { "601v", "601_v2" }, { "vanilla", "603" }, { "603e", "603e_v4.1" }, { "stretch", "603e_v4.1" }, diff --git a/target/ppc/cpu-models.h b/target/ppc/cpu-models.h index 612978a3fb..76775a74a9 100644 --- a/target/ppc/cpu-models.h +++ b/target/ppc/cpu-models.h @@ -205,9 +205,6 @@ enum { #define CPU_POWERPC_MPC8641 CPU_POWERPC_e600 #define CPU_POWERPC_MPC8641D CPU_POWERPC_e600 /* PowerPC 6xx cores */ - CPU_POWERPC_601_v0 = 0x00010001, - CPU_POWERPC_601_v1 = 0x00010001, - CPU_POWERPC_601_v2 = 0x00010002, CPU_POWERPC_603 = 0x00030100, CPU_POWERPC_603E_v11 = 0x00060101, CPU_POWERPC_603E_v12 = 0x00060102, diff --git a/target/ppc/cpu-qom.h b/target/ppc/cpu-qom.h index 99a6b509af..5d591ff6c5 100644 --- a/target/ppc/cpu-qom.h +++ b/target/ppc/cpu-qom.h @@ -61,8 +61,6 @@ enum powerpc_mmu_t { POWERPC_MMU_BOOKE = 0x00000008, /* BookE 2.06 MMU model */ POWERPC_MMU_BOOKE206 = 0x00000009, - /* PowerPC 601 MMU model (specific BATs format) */ - POWERPC_MMU_601 = 0x0000000A, #define POWERPC_MMU_64 0x00010000 /* 64 bits PowerPC MMU */ POWERPC_MMU_64B = POWERPC_MMU_64 | 0x00000001, @@ -90,10 +88,6 @@ enum powerpc_excp_t { POWERPC_EXCP_STD, /* PowerPC 40x exception model */ POWERPC_EXCP_40x, - /* PowerPC 601 exception model */ - POWERPC_EXCP_601, - /* PowerPC 602 exception model */ - POWERPC_EXCP_602, /* PowerPC 603 exception model */ POWERPC_EXCP_603, /* PowerPC G2 exception model */ diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index dcd83b503c..555c6b9245 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -89,11 +89,9 @@ enum { POWERPC_EXCP_VPU = 73, /* Vector unavailable exception */ /* 40x specific exceptions */ POWERPC_EXCP_PIT = 74, /* Programmable interval timer interrupt */ - /* 601 specific exceptions */ - POWERPC_EXCP_IO = 75, /* IO error exception */ - POWERPC_EXCP_RUNM = 76, /* Run mode exception */ + /* Vectors 75-76 are 601 specific exceptions */ /* 602 specific exceptions */ - POWERPC_EXCP_EMUL = 77, /* Emulation trap exception */ + POWERPC_EXCP_EMUL = 77, /* Emulation trap exception */ /* 602/603 specific exceptions */ POWERPC_EXCP_IFTLB = 78, /* Instruction fetch TLB miss */ POWERPC_EXCP_DLTLB = 79, /* Data load TLB miss */ @@ -632,8 +630,7 @@ enum { POWERPC_FLAG_PX = 0x00000200, POWERPC_FLAG_PMM = 0x00000400, /* Flag for special features */ - /* Decrementer clock: RTC clock (POWER, 601) or bus clock */ - POWERPC_FLAG_RTC_CLK = 0x00010000, + /* Decrementer clock */ POWERPC_FLAG_BUS_CLK = 0x00020000, /* Has CFAR */ POWERPC_FLAG_CFAR = 0x00040000, @@ -643,8 +640,6 @@ enum { POWERPC_FLAG_TM = 0x00100000, /* Has SCV (ISA 3.00) */ POWERPC_FLAG_SCV = 0x00200000, - /* Has HID0 for LE bit (601) */ - POWERPC_FLAG_HID0_LE = 0x00400000, }; /* @@ -655,7 +650,7 @@ enum { * the MSR are validated in hreg_compute_hflags. */ enum { - HFLAGS_LE = 0, /* MSR_LE -- comes from elsewhere on 601 */ + HFLAGS_LE = 0, /* MSR_LE */ HFLAGS_HV = 1, /* computed from MSR_HV and other state */ HFLAGS_64 = 2, /* computed from MSR_CE and MSR_SF */ HFLAGS_GTSE = 3, /* computed from SPR_LPCR[GTSE] */ @@ -1389,11 +1384,7 @@ void cpu_ppc_store_hdecr(CPUPPCState *env, target_ulong value); void cpu_ppc_store_tbu40(CPUPPCState *env, uint64_t value); uint64_t cpu_ppc_load_purr(CPUPPCState *env); void cpu_ppc_store_purr(CPUPPCState *env, uint64_t value); -uint32_t cpu_ppc601_load_rtcl(CPUPPCState *env); -uint32_t cpu_ppc601_load_rtcu(CPUPPCState *env); #if !defined(CONFIG_USER_ONLY) -void cpu_ppc601_store_rtcl(CPUPPCState *env, uint32_t value); -void cpu_ppc601_store_rtcu(CPUPPCState *env, uint32_t value); target_ulong load_40x_pit(CPUPPCState *env); void store_40x_pit(CPUPPCState *env, target_ulong val); void store_40x_dbcr0(CPUPPCState *env, uint32_t val); @@ -1516,17 +1507,12 @@ typedef PowerPCCPU ArchCPU; /* SPR definitions */ #define SPR_MQ (0x000) #define SPR_XER (0x001) -#define SPR_601_VRTCU (0x004) -#define SPR_601_VRTCL (0x005) -#define SPR_601_UDECR (0x006) #define SPR_LR (0x008) #define SPR_CTR (0x009) #define SPR_UAMR (0x00D) #define SPR_DSCR (0x011) #define SPR_DSISR (0x012) -#define SPR_DAR (0x013) /* DAE for PowerPC 601 */ -#define SPR_601_RTCU (0x014) -#define SPR_601_RTCL (0x015) +#define SPR_DAR (0x013) #define SPR_DECR (0x016) #define SPR_SDR1 (0x019) #define SPR_SRR0 (0x01A) @@ -2003,7 +1989,6 @@ typedef PowerPCCPU ArchCPU; #define SPR_HID1 (0x3F1) #define SPR_IABR (0x3F2) #define SPR_40x_DBCR0 (0x3F2) -#define SPR_601_HID2 (0x3F2) #define SPR_Exxx_L1CSR0 (0x3F2) #define SPR_ICTRL (0x3F3) #define SPR_HID2 (0x3F3) @@ -2019,7 +2004,6 @@ typedef PowerPCCPU ArchCPU; #define DABR_MASK (~(target_ulong)0x7) #define SPR_Exxx_BUCSR (0x3F5) #define SPR_40x_IAC2 (0x3F5) -#define SPR_601_HID5 (0x3F5) #define SPR_40x_DAC1 (0x3F6) #define SPR_MSSCR0 (0x3F6) #define SPR_970_HID5 (0x3F6) @@ -2052,7 +2036,6 @@ typedef PowerPCCPU ArchCPU; #define SPR_403_PBL2 (0x3FE) #define SPR_PIR (0x3FF) #define SPR_403_PBU2 (0x3FF) -#define SPR_601_HID15 (0x3FF) #define SPR_604_HID15 (0x3FF) #define SPR_E500_SVR (0x3FF) @@ -2117,15 +2100,6 @@ enum { #define PPC_RES PPC_INSNS_BASE /* spr/msr access instructions */ #define PPC_MISC PPC_INSNS_BASE - /* Deprecated instruction sets */ - /* Original POWER instruction set */ - PPC_POWER = 0x0000000000000002ULL, - /* POWER2 instruction set extension */ - PPC_POWER2 = 0x0000000000000004ULL, - /* Power RTC support */ - PPC_POWER_RTC = 0x0000000000000008ULL, - /* Power-to-PowerPC bridge (601) */ - PPC_POWER_BR = 0x0000000000000010ULL, /* 64 bits PowerPC instruction set */ PPC_64B = 0x0000000000000020ULL, /* New 64 bits extensions (PowerPC 2.0x) */ @@ -2236,8 +2210,7 @@ enum { /* popcntw and popcntd instructions */ PPC_POPCNTWD = 0x8000000000000000ULL, -#define PPC_TCG_INSNS (PPC_INSNS_BASE | PPC_POWER | PPC_POWER2 \ - | PPC_POWER_RTC | PPC_POWER_BR | PPC_64B \ +#define PPC_TCG_INSNS (PPC_INSNS_BASE | PPC_64B \ | PPC_64BX | PPC_64H | PPC_WAIT | PPC_MFTB \ | PPC_ISEL | PPC_POPCNTB \ | PPC_STRING | PPC_FLOAT | PPC_FLOAT_EXT \ diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index b4f0835849..a5e1f5a3b2 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -749,83 +749,6 @@ static void register_G2_sprs(CPUPPCState *env) 0x00000000); } -/* SPR specific to PowerPC 601 implementation */ -static void register_601_sprs(CPUPPCState *env) -{ - /* Multiplication/division register */ - /* MQ */ - spr_register(env, SPR_MQ, "MQ", - &spr_read_generic, &spr_write_generic, - &spr_read_generic, &spr_write_generic, - 0x00000000); - /* RTC registers */ - spr_register(env, SPR_601_RTCU, "RTCU", - SPR_NOACCESS, SPR_NOACCESS, - SPR_NOACCESS, &spr_write_601_rtcu, - 0x00000000); - spr_register(env, SPR_601_VRTCU, "RTCU", - &spr_read_601_rtcu, SPR_NOACCESS, - &spr_read_601_rtcu, SPR_NOACCESS, - 0x00000000); - spr_register(env, SPR_601_RTCL, "RTCL", - SPR_NOACCESS, SPR_NOACCESS, - SPR_NOACCESS, &spr_write_601_rtcl, - 0x00000000); - spr_register(env, SPR_601_VRTCL, "RTCL", - &spr_read_601_rtcl, SPR_NOACCESS, - &spr_read_601_rtcl, SPR_NOACCESS, - 0x00000000); - /* Timer */ -#if 0 /* ? */ - spr_register(env, SPR_601_UDECR, "UDECR", - &spr_read_decr, SPR_NOACCESS, - &spr_read_decr, SPR_NOACCESS, - 0x00000000); -#endif - /* External access control */ - /* XXX : not implemented */ - spr_register(env, SPR_EAR, "EAR", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, - 0x00000000); - /* Memory management */ -#if !defined(CONFIG_USER_ONLY) - spr_register(env, SPR_IBAT0U, "IBAT0U", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_601_ubat, &spr_write_601_ubatu, - 0x00000000); - spr_register(env, SPR_IBAT0L, "IBAT0L", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_601_ubat, &spr_write_601_ubatl, - 0x00000000); - spr_register(env, SPR_IBAT1U, "IBAT1U", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_601_ubat, &spr_write_601_ubatu, - 0x00000000); - spr_register(env, SPR_IBAT1L, "IBAT1L", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_601_ubat, &spr_write_601_ubatl, - 0x00000000); - spr_register(env, SPR_IBAT2U, "IBAT2U", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_601_ubat, &spr_write_601_ubatu, - 0x00000000); - spr_register(env, SPR_IBAT2L, "IBAT2L", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_601_ubat, &spr_write_601_ubatl, - 0x00000000); - spr_register(env, SPR_IBAT3U, "IBAT3U", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_601_ubat, &spr_write_601_ubatu, - 0x00000000); - spr_register(env, SPR_IBAT3L, "IBAT3L", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_601_ubat, &spr_write_601_ubatl, - 0x00000000); - env->nb_BATs = 4; -#endif -} - static void register_74xx_sprs(CPUPPCState *env) { /* Processor identification */ @@ -2060,26 +1983,6 @@ static void init_excp_BookE(CPUPPCState *env) #endif } -static void init_excp_601(CPUPPCState *env) -{ -#if !defined(CONFIG_USER_ONLY) - env->excp_vectors[POWERPC_EXCP_RESET] = 0x00000100; - env->excp_vectors[POWERPC_EXCP_MCHECK] = 0x00000200; - env->excp_vectors[POWERPC_EXCP_DSI] = 0x00000300; - env->excp_vectors[POWERPC_EXCP_ISI] = 0x00000400; - env->excp_vectors[POWERPC_EXCP_EXTERNAL] = 0x00000500; - env->excp_vectors[POWERPC_EXCP_ALIGN] = 0x00000600; - env->excp_vectors[POWERPC_EXCP_PROGRAM] = 0x00000700; - env->excp_vectors[POWERPC_EXCP_FPU] = 0x00000800; - env->excp_vectors[POWERPC_EXCP_DECR] = 0x00000900; - env->excp_vectors[POWERPC_EXCP_IO] = 0x00000A00; - env->excp_vectors[POWERPC_EXCP_SYSCALL] = 0x00000C00; - env->excp_vectors[POWERPC_EXCP_RUNM] = 0x00002000; - /* Hardware reset vector */ - env->hreset_vector = 0x00000100UL; -#endif -} - static void init_excp_603(CPUPPCState *env) { #if !defined(CONFIG_USER_ONLY) @@ -3809,120 +3712,6 @@ POWERPC_FAMILY(e6500)(ObjectClass *oc, void *data) #endif /* Non-embedded PowerPC */ - -#define POWERPC_MSRR_601 (0x0000000000001040ULL) - -static void init_proc_601(CPUPPCState *env) -{ - register_ne_601_sprs(env); - register_sdr1_sprs(env); - register_601_sprs(env); - /* Hardware implementation registers */ - /* XXX : not implemented */ - spr_register(env, SPR_HID0, "HID0", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_hid0_601, - 0x80010080); - /* XXX : not implemented */ - spr_register(env, SPR_HID1, "HID1", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, - 0x00000000); - /* XXX : not implemented */ - spr_register(env, SPR_601_HID2, "HID2", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, - 0x00000000); - /* XXX : not implemented */ - spr_register(env, SPR_601_HID5, "HID5", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, - 0x00000000); - /* Memory management */ - init_excp_601(env); - /* - * XXX: beware that dcache line size is 64 - * but dcbz uses 32 bytes "sectors" - * XXX: this breaks clcs instruction ! - */ - env->dcache_line_size = 32; - env->icache_line_size = 64; - /* Allocate hardware IRQ controller */ - ppc6xx_irq_init(env_archcpu(env)); -} - -POWERPC_FAMILY(601)(ObjectClass *oc, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(oc); - PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); - - dc->desc = "PowerPC 601"; - pcc->init_proc = init_proc_601; - pcc->check_pow = check_pow_none; - pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_POWER_BR | - PPC_FLOAT | - PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | - PPC_MEM_SYNC | PPC_MEM_EIEIO | PPC_MEM_TLBIE | - PPC_SEGMENT | PPC_EXTERN; - pcc->msr_mask = (1ull << MSR_EE) | - (1ull << MSR_PR) | - (1ull << MSR_FP) | - (1ull << MSR_ME) | - (1ull << MSR_FE0) | - (1ull << MSR_SE) | - (1ull << MSR_FE1) | - (1ull << MSR_EP) | - (1ull << MSR_IR) | - (1ull << MSR_DR); - pcc->mmu_model = POWERPC_MMU_601; - pcc->excp_model = POWERPC_EXCP_601; - pcc->bus_model = PPC_FLAGS_INPUT_6xx; - pcc->bfd_mach = bfd_mach_ppc_601; - pcc->flags = POWERPC_FLAG_SE | POWERPC_FLAG_RTC_CLK | POWERPC_FLAG_HID0_LE; -} - -#define POWERPC_MSRR_601v (0x0000000000001040ULL) - -static void init_proc_601v(CPUPPCState *env) -{ - init_proc_601(env); - /* XXX : not implemented */ - spr_register(env, SPR_601_HID15, "HID15", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, - 0x00000000); -} - -POWERPC_FAMILY(601v)(ObjectClass *oc, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(oc); - PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); - - dc->desc = "PowerPC 601v"; - pcc->init_proc = init_proc_601v; - pcc->check_pow = check_pow_none; - pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_POWER_BR | - PPC_FLOAT | - PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | - PPC_MEM_SYNC | PPC_MEM_EIEIO | PPC_MEM_TLBIE | - PPC_SEGMENT | PPC_EXTERN; - pcc->msr_mask = (1ull << MSR_EE) | - (1ull << MSR_PR) | - (1ull << MSR_FP) | - (1ull << MSR_ME) | - (1ull << MSR_FE0) | - (1ull << MSR_SE) | - (1ull << MSR_FE1) | - (1ull << MSR_EP) | - (1ull << MSR_IR) | - (1ull << MSR_DR); - pcc->mmu_model = POWERPC_MMU_601; - pcc->excp_model = POWERPC_EXCP_601; - pcc->bus_model = PPC_FLAGS_INPUT_6xx; - pcc->bfd_mach = bfd_mach_ppc_601; - pcc->flags = POWERPC_FLAG_SE | POWERPC_FLAG_RTC_CLK | POWERPC_FLAG_HID0_LE; -} - static void init_proc_603(CPUPPCState *env) { register_ne_601_sprs(env); @@ -7677,7 +7466,7 @@ static void init_ppc_proc(PowerPCCPU *cpu) "Should not define POWERPC_FLAG_PX nor POWERPC_FLAG_PMM\n"); exit(1); } - if ((env->flags & (POWERPC_FLAG_RTC_CLK | POWERPC_FLAG_BUS_CLK)) == 0) { + if ((env->flags & POWERPC_FLAG_BUS_CLK) == 0) { fprintf(stderr, "PowerPC flags inconsistency\n" "Should define the time-base and decrementer clock source\n"); exit(1); @@ -8491,7 +8280,6 @@ void ppc_cpu_dump_state(CPUState *cs, FILE *f, int flags) switch (env->mmu_model) { case POWERPC_MMU_32B: - case POWERPC_MMU_601: case POWERPC_MMU_SOFT_6xx: #if defined(TARGET_PPC64) case POWERPC_MMU_64B: diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index 7d7d0a08b5..ae6871a3c0 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -73,8 +73,6 @@ static const char *powerpc_excp_name(int excp) case POWERPC_EXCP_HISEG: return "HISEG"; case POWERPC_EXCP_VPU: return "VPU"; case POWERPC_EXCP_PIT: return "PIT"; - case POWERPC_EXCP_IO: return "IO"; - case POWERPC_EXCP_RUNM: return "RUNM"; case POWERPC_EXCP_EMUL: return "EMUL"; case POWERPC_EXCP_IFTLB: return "IFTLB"; case POWERPC_EXCP_DLTLB: return "DLTLB"; @@ -1671,9 +1669,6 @@ static inline void powerpc_excp_legacy(PowerPCCPU *cpu, int excp) case POWERPC_EXCP_EFPDI: /* Embedded floating-point data interrupt */ case POWERPC_EXCP_EFPRI: /* Embedded floating-point round interrupt */ case POWERPC_EXCP_EPERFM: /* Embedded performance monitor interrupt */ - case POWERPC_EXCP_IO: /* IO error exception */ - case POWERPC_EXCP_RUNM: /* Run mode exception */ - case POWERPC_EXCP_EMUL: /* Emulation trap exception */ case POWERPC_EXCP_FPA: /* Floating-point assist exception */ case POWERPC_EXCP_DABR: /* Data address breakpoint */ case POWERPC_EXCP_IABR: /* Instruction address breakpoint */ @@ -2202,14 +2197,8 @@ void helper_td(CPUPPCState *env, target_ulong arg1, target_ulong arg2, #endif #if !defined(CONFIG_USER_ONLY) -/*****************************************************************************/ -/* PowerPC 601 specific instructions (POWER bridge) */ #ifdef CONFIG_TCG -void helper_rfsvc(CPUPPCState *env) -{ - do_rfi(env, env->lr, env->ctr & 0x0000FFFF); -} /* Embedded.Processor Control */ static int dbell2irq(target_ulong rb) diff --git a/target/ppc/helper.h b/target/ppc/helper.h index f2e5060910..ab008c9d4e 100644 --- a/target/ppc/helper.h +++ b/target/ppc/helper.h @@ -7,7 +7,6 @@ DEF_HELPER_FLAGS_4(td, TCG_CALL_NO_WG, void, env, tl, tl, i32) #if !defined(CONFIG_USER_ONLY) DEF_HELPER_2(store_msr, void, env, tl) DEF_HELPER_1(rfi, void, env) -DEF_HELPER_1(rfsvc, void, env) DEF_HELPER_1(40x_rfci, void, env) DEF_HELPER_1(rfci, void, env) DEF_HELPER_1(rfdi, void, env) @@ -653,14 +652,9 @@ DEF_HELPER_2(book3s_msgclr, void, env, tl) #endif DEF_HELPER_4(dlmzb, tl, env, tl, tl, i32) -DEF_HELPER_FLAGS_2(clcs, TCG_CALL_NO_RWG_SE, tl, env, i32) #if !defined(CONFIG_USER_ONLY) DEF_HELPER_2(rac, tl, env, tl) #endif -DEF_HELPER_3(div, tl, env, tl, tl) -DEF_HELPER_3(divo, tl, env, tl, tl) -DEF_HELPER_3(divs, tl, env, tl, tl) -DEF_HELPER_3(divso, tl, env, tl, tl) DEF_HELPER_2(load_dcr, tl, env, tl) DEF_HELPER_3(store_dcr, void, env, tl, tl) @@ -674,8 +668,6 @@ DEF_HELPER_FLAGS_1(load_tbu, TCG_CALL_NO_RWG, tl, env) DEF_HELPER_FLAGS_1(load_atbl, TCG_CALL_NO_RWG, tl, env) DEF_HELPER_FLAGS_1(load_atbu, TCG_CALL_NO_RWG, tl, env) DEF_HELPER_FLAGS_1(load_vtb, TCG_CALL_NO_RWG, tl, env) -DEF_HELPER_FLAGS_1(load_601_rtcl, TCG_CALL_NO_RWG, tl, env) -DEF_HELPER_FLAGS_1(load_601_rtcu, TCG_CALL_NO_RWG, tl, env) #if !defined(CONFIG_USER_ONLY) #if defined(TARGET_PPC64) DEF_HELPER_FLAGS_1(load_purr, TCG_CALL_NO_RWG, tl, env) @@ -693,15 +685,12 @@ DEF_HELPER_FLAGS_2(store_tbl, TCG_CALL_NO_RWG, void, env, tl) DEF_HELPER_FLAGS_2(store_tbu, TCG_CALL_NO_RWG, void, env, tl) DEF_HELPER_FLAGS_2(store_atbl, TCG_CALL_NO_RWG, void, env, tl) DEF_HELPER_FLAGS_2(store_atbu, TCG_CALL_NO_RWG, void, env, tl) -DEF_HELPER_FLAGS_2(store_601_rtcl, TCG_CALL_NO_RWG, void, env, tl) -DEF_HELPER_FLAGS_2(store_601_rtcu, TCG_CALL_NO_RWG, void, env, tl) DEF_HELPER_FLAGS_1(load_decr, TCG_CALL_NO_RWG, tl, env) DEF_HELPER_FLAGS_2(store_decr, TCG_CALL_NO_RWG, void, env, tl) DEF_HELPER_FLAGS_1(load_hdecr, TCG_CALL_NO_RWG, tl, env) DEF_HELPER_FLAGS_2(store_hdecr, TCG_CALL_NO_RWG, void, env, tl) DEF_HELPER_FLAGS_2(store_vtb, TCG_CALL_NO_RWG, void, env, tl) DEF_HELPER_FLAGS_2(store_tbu40, TCG_CALL_NO_RWG, void, env, tl) -DEF_HELPER_2(store_hid0_601, void, env, tl) DEF_HELPER_FLAGS_1(load_40x_pit, TCG_CALL_NO_RWG, tl, env) DEF_HELPER_FLAGS_2(store_40x_pit, TCG_CALL_NO_RWG, void, env, tl) DEF_HELPER_FLAGS_2(store_40x_tcr, TCG_CALL_NO_RWG, void, env, tl) @@ -715,8 +704,6 @@ DEF_HELPER_3(store_ibatl, void, env, i32, tl) DEF_HELPER_3(store_ibatu, void, env, i32, tl) DEF_HELPER_3(store_dbatl, void, env, i32, tl) DEF_HELPER_3(store_dbatu, void, env, i32, tl) -DEF_HELPER_3(store_601_batl, void, env, i32, tl) -DEF_HELPER_3(store_601_batu, void, env, i32, tl) #endif #define dh_alias_fprp ptr diff --git a/target/ppc/helper_regs.c b/target/ppc/helper_regs.c index 5b12cb03c9..38fcb5fe50 100644 --- a/target/ppc/helper_regs.c +++ b/target/ppc/helper_regs.c @@ -59,15 +59,6 @@ static uint32_t hreg_compute_hflags_value(CPUPPCState *env) msr_mask = ((1 << MSR_LE) | (1 << MSR_PR) | (1 << MSR_DR) | (1 << MSR_FP)); - if (ppc_flags & POWERPC_FLAG_HID0_LE) { - /* - * Note that MSR_LE is not set in env->msr_mask for this cpu, - * and so will never be set in msr. - */ - uint32_t le = extract32(env->spr[SPR_HID0], 3, 1); - hflags |= le << MSR_LE; - } - if (ppc_flags & POWERPC_FLAG_DE) { target_ulong dbcr0 = env->spr[SPR_BOOKE_DBCR0]; if (dbcr0 & DBCR0_ICMP) { @@ -249,7 +240,6 @@ int hreg_store_msr(CPUPPCState *env, target_ulong value, int alter_hv) hreg_swap_gpr_tgpr(env); } if (unlikely((value >> MSR_EP) & 1) != msr_ep) { - /* Change the exception prefix on PowerPC 601 */ env->excp_prefix = ((value >> MSR_EP) & 1) * 0xFFF00000; } /* diff --git a/target/ppc/int_helper.c b/target/ppc/int_helper.c index d7765fd3e3..d1b12788b2 100644 --- a/target/ppc/int_helper.c +++ b/target/ppc/int_helper.c @@ -422,72 +422,6 @@ uint64_t helper_PEXTD(uint64_t src, uint64_t mask) return result; } -/*****************************************************************************/ -/* PowerPC 601 specific instructions (POWER bridge) */ -target_ulong helper_div(CPUPPCState *env, target_ulong arg1, target_ulong arg2) -{ - uint64_t tmp = (uint64_t)arg1 << 32 | env->spr[SPR_MQ]; - - if (((int32_t)tmp == INT32_MIN && (int32_t)arg2 == (int32_t)-1) || - (int32_t)arg2 == 0) { - env->spr[SPR_MQ] = 0; - return INT32_MIN; - } else { - env->spr[SPR_MQ] = tmp % arg2; - return tmp / (int32_t)arg2; - } -} - -target_ulong helper_divo(CPUPPCState *env, target_ulong arg1, - target_ulong arg2) -{ - uint64_t tmp = (uint64_t)arg1 << 32 | env->spr[SPR_MQ]; - - if (((int32_t)tmp == INT32_MIN && (int32_t)arg2 == (int32_t)-1) || - (int32_t)arg2 == 0) { - env->so = env->ov = 1; - env->spr[SPR_MQ] = 0; - return INT32_MIN; - } else { - env->spr[SPR_MQ] = tmp % arg2; - tmp /= (int32_t)arg2; - if ((int32_t)tmp != tmp) { - env->so = env->ov = 1; - } else { - env->ov = 0; - } - return tmp; - } -} - -target_ulong helper_divs(CPUPPCState *env, target_ulong arg1, - target_ulong arg2) -{ - if (((int32_t)arg1 == INT32_MIN && (int32_t)arg2 == (int32_t)-1) || - (int32_t)arg2 == 0) { - env->spr[SPR_MQ] = 0; - return INT32_MIN; - } else { - env->spr[SPR_MQ] = (int32_t)arg1 % (int32_t)arg2; - return (int32_t)arg1 / (int32_t)arg2; - } -} - -target_ulong helper_divso(CPUPPCState *env, target_ulong arg1, - target_ulong arg2) -{ - if (((int32_t)arg1 == INT32_MIN && (int32_t)arg2 == (int32_t)-1) || - (int32_t)arg2 == 0) { - env->so = env->ov = 1; - env->spr[SPR_MQ] = 0; - return INT32_MIN; - } else { - env->ov = 0; - env->spr[SPR_MQ] = (int32_t)arg1 % (int32_t)arg2; - return (int32_t)arg1 / (int32_t)arg2; - } -} - /*****************************************************************************/ /* Altivec extension helpers */ #if defined(HOST_WORDS_BIGENDIAN) diff --git a/target/ppc/machine.c b/target/ppc/machine.c index a503e00ddc..1b63146ed1 100644 --- a/target/ppc/machine.c +++ b/target/ppc/machine.c @@ -205,9 +205,8 @@ static int cpu_pre_save(void *opaque) } } - /* Retain migration compatibility for pre 6.0 for 601 machines. */ - env->hflags_compat_nmsr = (env->flags & POWERPC_FLAG_HID0_LE - ? env->hflags & MSR_LE : 0); + /* Used to retain migration compatibility for pre 6.0 for 601 machines. */ + env->hflags_compat_nmsr = 0; return 0; } diff --git a/target/ppc/misc_helper.c b/target/ppc/misc_helper.c index 1bcefa7c84..29e73a6ffd 100644 --- a/target/ppc/misc_helper.c +++ b/target/ppc/misc_helper.c @@ -211,21 +211,6 @@ void helper_store_lpidr(CPUPPCState *env, target_ulong val) tlb_flush(env_cpu(env)); } -void helper_store_hid0_601(CPUPPCState *env, target_ulong val) -{ - target_ulong hid0; - - hid0 = env->spr[SPR_HID0]; - env->spr[SPR_HID0] = (uint32_t)val; - - if ((val ^ hid0) & 0x00000008) { - /* Change current endianness */ - hreg_compute_hflags(env); - qemu_log("%s: set endianness to %c => %08x\n", __func__, - val & 0x8 ? 'l' : 'b', env->hflags); - } -} - void helper_store_40x_dbcr0(CPUPPCState *env, target_ulong val) { /* Bits 26 & 27 affect single-stepping. */ @@ -239,31 +224,6 @@ void helper_store_40x_sler(CPUPPCState *env, target_ulong val) store_40x_sler(env, val); } #endif -/*****************************************************************************/ -/* PowerPC 601 specific instructions (POWER bridge) */ - -target_ulong helper_clcs(CPUPPCState *env, uint32_t arg) -{ - switch (arg) { - case 0x0CUL: - /* Instruction cache line size */ - return env->icache_line_size; - case 0x0DUL: - /* Data cache line size */ - return env->dcache_line_size; - case 0x0EUL: - /* Minimum cache line size */ - return (env->icache_line_size < env->dcache_line_size) ? - env->icache_line_size : env->dcache_line_size; - case 0x0FUL: - /* Maximum cache line size */ - return (env->icache_line_size > env->dcache_line_size) ? - env->icache_line_size : env->dcache_line_size; - default: - /* Undefined */ - return 0; - } -} /*****************************************************************************/ /* Special registers manipulation */ diff --git a/target/ppc/mmu-hash32.c b/target/ppc/mmu-hash32.c index 3957aab2dc..cc091c3e62 100644 --- a/target/ppc/mmu-hash32.c +++ b/target/ppc/mmu-hash32.c @@ -125,30 +125,6 @@ static int hash32_bat_prot(PowerPCCPU *cpu, return prot; } -static target_ulong hash32_bat_601_size(PowerPCCPU *cpu, - target_ulong batu, target_ulong batl) -{ - if (!(batl & BATL32_601_V)) { - return 0; - } - - return BATU32_BEPI & ~((batl & BATL32_601_BL) << 17); -} - -static int hash32_bat_601_prot(int mmu_idx, - target_ulong batu, target_ulong batl) -{ - int key, pp; - - pp = batu & BATU32_601_PP; - if (mmuidx_pr(mmu_idx) == 0) { - key = !!(batu & BATU32_601_KS); - } else { - key = !!(batu & BATU32_601_KP); - } - return ppc_hash32_pp_prot(key, pp, 0); -} - static hwaddr ppc_hash32_bat_lookup(PowerPCCPU *cpu, target_ulong ea, MMUAccessType access_type, int *prot, int mmu_idx) @@ -172,11 +148,7 @@ static hwaddr ppc_hash32_bat_lookup(PowerPCCPU *cpu, target_ulong ea, target_ulong batl = BATlt[i]; target_ulong mask; - if (unlikely(env->mmu_model == POWERPC_MMU_601)) { - mask = hash32_bat_601_size(cpu, batu, batl); - } else { - mask = hash32_bat_size(mmu_idx, batu, batl); - } + mask = hash32_bat_size(mmu_idx, batu, batl); LOG_BATS("%s: %cBAT%d v " TARGET_FMT_lx " BATu " TARGET_FMT_lx " BATl " TARGET_FMT_lx "\n", __func__, ifetch ? 'I' : 'D', i, ea, batu, batl); @@ -184,11 +156,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); - if (unlikely(env->mmu_model == POWERPC_MMU_601)) { - *prot = hash32_bat_601_prot(mmu_idx, batu, batl); - } else { - *prot = hash32_bat_prot(cpu, batu, batl); - } + *prot = hash32_bat_prot(cpu, batu, batl); return raddr & TARGET_PAGE_MASK; } @@ -231,18 +199,6 @@ static bool ppc_hash32_direct_store(PowerPCCPU *cpu, target_ulong sr, qemu_log_mask(CPU_LOG_MMU, "direct store...\n"); - if ((sr & 0x1FF00000) >> 20 == 0x07f) { - /* - * Memory-forced I/O controller interface access - * - * If T=1 and BUID=x'07F', the 601 performs a memory access - * to SR[28-31] LA[4-31], bypassing all protection mechanisms. - */ - *raddr = ((sr & 0xF) << 28) | (eaddr & 0x0FFFFFFF); - *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; - return true; - } - if (access_type == MMU_INST_FETCH) { /* No code fetch is allowed in direct-store areas */ if (guest_visible) { diff --git a/target/ppc/mmu-hash32.h b/target/ppc/mmu-hash32.h index 3892b693d6..7119a63d97 100644 --- a/target/ppc/mmu-hash32.h +++ b/target/ppc/mmu-hash32.h @@ -34,15 +34,6 @@ bool ppc_hash32_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, #define BATL32_WIMG 0x00000078 #define BATL32_PP 0x00000003 -/* PowerPC 601 has slightly different BAT registers */ - -#define BATU32_601_KS 0x00000008 -#define BATU32_601_KP 0x00000004 -#define BATU32_601_PP 0x00000003 - -#define BATL32_601_V 0x00000040 -#define BATL32_601_BL 0x0000003f - /* * Hash page table definitions */ diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index 6512ee031c..e9c5b14c0f 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -441,29 +441,9 @@ static int get_segment_6xx_tlb(CPUPPCState *env, mmu_ctx_t *ctx, ret = -3; } } else { - target_ulong sr; - qemu_log_mask(CPU_LOG_MMU, "direct store...\n"); /* Direct-store segment : absolutely *BUGGY* for now */ - /* - * Direct-store implies a 32-bit MMU. - * Check the Segment Register's bus unit ID (BUID). - */ - sr = env->sr[eaddr >> 28]; - if ((sr & 0x1FF00000) >> 20 == 0x07f) { - /* - * Memory-forced I/O controller interface access - * - * If T=1 and BUID=x'07F', the 601 performs a memory - * access to SR[28-31] LA[4-31], bypassing all protection - * mechanisms. - */ - ctx->raddr = ((sr & 0xF) << 28) | (eaddr & 0x0FFFFFFF); - ctx->prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; - return 0; - } - switch (type) { case ACCESS_INT: /* Integer load/store : only access allowed */ @@ -1539,7 +1519,6 @@ bool ppc_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, #endif case POWERPC_MMU_32B: - case POWERPC_MMU_601: return ppc_hash32_xlate(cpu, eaddr, access_type, raddrp, psizep, protp, mmu_idx, guest_visible); diff --git a/target/ppc/mmu_helper.c b/target/ppc/mmu_helper.c index a2a52a12c3..142a717255 100644 --- a/target/ppc/mmu_helper.c +++ b/target/ppc/mmu_helper.c @@ -279,88 +279,6 @@ void helper_store_dbatl(CPUPPCState *env, uint32_t nr, target_ulong value) env->DBAT[1][nr] = value; } -void helper_store_601_batu(CPUPPCState *env, uint32_t nr, target_ulong value) -{ - target_ulong mask; -#if defined(FLUSH_ALL_TLBS) - int do_inval; -#endif - - dump_store_bat(env, 'I', 0, nr, value); - if (env->IBAT[0][nr] != value) { -#if defined(FLUSH_ALL_TLBS) - do_inval = 0; -#endif - mask = (env->IBAT[1][nr] << 17) & 0x0FFE0000UL; - if (env->IBAT[1][nr] & 0x40) { - /* Invalidate BAT only if it is valid */ -#if !defined(FLUSH_ALL_TLBS) - do_invalidate_BAT(env, env->IBAT[0][nr], mask); -#else - do_inval = 1; -#endif - } - /* - * When storing valid upper BAT, mask BEPI and BRPN and - * invalidate all TLBs covered by this BAT - */ - env->IBAT[0][nr] = (value & 0x00001FFFUL) | - (value & ~0x0001FFFFUL & ~mask); - env->DBAT[0][nr] = env->IBAT[0][nr]; - if (env->IBAT[1][nr] & 0x40) { -#if !defined(FLUSH_ALL_TLBS) - do_invalidate_BAT(env, env->IBAT[0][nr], mask); -#else - do_inval = 1; -#endif - } -#if defined(FLUSH_ALL_TLBS) - if (do_inval) { - tlb_flush(env_cpu(env)); - } -#endif - } -} - -void helper_store_601_batl(CPUPPCState *env, uint32_t nr, target_ulong value) -{ -#if !defined(FLUSH_ALL_TLBS) - target_ulong mask; -#else - int do_inval; -#endif - - dump_store_bat(env, 'I', 1, nr, value); - if (env->IBAT[1][nr] != value) { -#if defined(FLUSH_ALL_TLBS) - do_inval = 0; -#endif - if (env->IBAT[1][nr] & 0x40) { -#if !defined(FLUSH_ALL_TLBS) - mask = (env->IBAT[1][nr] << 17) & 0x0FFE0000UL; - do_invalidate_BAT(env, env->IBAT[0][nr], mask); -#else - do_inval = 1; -#endif - } - if (value & 0x40) { -#if !defined(FLUSH_ALL_TLBS) - mask = (value << 17) & 0x0FFE0000UL; - do_invalidate_BAT(env, env->IBAT[0][nr], mask); -#else - do_inval = 1; -#endif - } - env->IBAT[1][nr] = value; - env->DBAT[1][nr] = value; -#if defined(FLUSH_ALL_TLBS) - if (do_inval) { - tlb_flush(env_cpu(env)); - } -#endif - } -} - /*****************************************************************************/ /* TLB management */ void ppc_tlb_invalidate_all(CPUPPCState *env) @@ -392,7 +310,6 @@ void ppc_tlb_invalidate_all(CPUPPCState *env) booke206_flush_tlb(env, -1, 0); break; case POWERPC_MMU_32B: - case POWERPC_MMU_601: env->tlb_need_flush = 0; tlb_flush(env_cpu(env)); break; @@ -426,7 +343,6 @@ void ppc_tlb_invalidate_one(CPUPPCState *env, target_ulong addr) } break; case POWERPC_MMU_32B: - case POWERPC_MMU_601: /* * Actual CPUs invalidate entire congruence classes based on * the geometry of their TLBs and some OSes take that into diff --git a/target/ppc/spr_tcg.h b/target/ppc/spr_tcg.h index 89ff111724..df2abacc64 100644 --- a/target/ppc/spr_tcg.h +++ b/target/ppc/spr_tcg.h @@ -45,8 +45,6 @@ void spr_read_tbl(DisasContext *ctx, int gprn, int sprn); void spr_read_tbu(DisasContext *ctx, int gprn, int sprn); void spr_read_atbl(DisasContext *ctx, int gprn, int sprn); void spr_read_atbu(DisasContext *ctx, int gprn, int sprn); -void spr_read_601_rtcl(DisasContext *ctx, int gprn, int sprn); -void spr_read_601_rtcu(DisasContext *ctx, int gprn, int sprn); void spr_read_spefscr(DisasContext *ctx, int gprn, int sprn); void spr_write_spefscr(DisasContext *ctx, int sprn, int gprn); void spr_write_MMCR0_ureg(DisasContext *ctx, int sprn, int gprn); @@ -77,12 +75,6 @@ void spr_write_dbatu_h(DisasContext *ctx, int sprn, int gprn); void spr_write_dbatl(DisasContext *ctx, int sprn, int gprn); void spr_write_dbatl_h(DisasContext *ctx, int sprn, int gprn); void spr_write_sdr1(DisasContext *ctx, int sprn, int gprn); -void spr_write_601_rtcu(DisasContext *ctx, int sprn, int gprn); -void spr_write_601_rtcl(DisasContext *ctx, int sprn, int gprn); -void spr_write_hid0_601(DisasContext *ctx, int sprn, int gprn); -void spr_read_601_ubat(DisasContext *ctx, int gprn, int sprn); -void spr_write_601_ubatu(DisasContext *ctx, int sprn, int gprn); -void spr_write_601_ubatl(DisasContext *ctx, int sprn, int gprn); void spr_read_40x_pit(DisasContext *ctx, int gprn, int sprn); void spr_write_40x_pit(DisasContext *ctx, int sprn, int gprn); void spr_write_40x_dbcr0(DisasContext *ctx, int sprn, int gprn); diff --git a/target/ppc/timebase_helper.c b/target/ppc/timebase_helper.c index af378318c1..86d01d6e4e 100644 --- a/target/ppc/timebase_helper.c +++ b/target/ppc/timebase_helper.c @@ -63,16 +63,6 @@ void helper_store_purr(CPUPPCState *env, target_ulong val) } #endif -target_ulong helper_load_601_rtcl(CPUPPCState *env) -{ - return cpu_ppc601_load_rtcl(env); -} - -target_ulong helper_load_601_rtcu(CPUPPCState *env) -{ - return cpu_ppc601_load_rtcu(env); -} - #if !defined(CONFIG_USER_ONLY) void helper_store_tbl(CPUPPCState *env, target_ulong val) { @@ -94,16 +84,6 @@ void helper_store_atbu(CPUPPCState *env, target_ulong val) cpu_ppc_store_atbu(env, val); } -void helper_store_601_rtcl(CPUPPCState *env, target_ulong val) -{ - cpu_ppc601_store_rtcl(env, val); -} - -void helper_store_601_rtcu(CPUPPCState *env, target_ulong val) -{ - cpu_ppc601_store_rtcu(env, val); -} - target_ulong helper_load_decr(CPUPPCState *env) { return cpu_ppc_load_decr(env); diff --git a/target/ppc/translate.c b/target/ppc/translate.c index c2f436f8d3..2eaffd432a 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -794,61 +794,6 @@ void spr_write_dpdes(DisasContext *ctx, int sprn, int gprn) #endif #endif -/* PowerPC 601 specific registers */ -/* RTC */ -void spr_read_601_rtcl(DisasContext *ctx, int gprn, int sprn) -{ - gen_helper_load_601_rtcl(cpu_gpr[gprn], cpu_env); -} - -void spr_read_601_rtcu(DisasContext *ctx, int gprn, int sprn) -{ - gen_helper_load_601_rtcu(cpu_gpr[gprn], cpu_env); -} - -#if !defined(CONFIG_USER_ONLY) -void spr_write_601_rtcu(DisasContext *ctx, int sprn, int gprn) -{ - gen_helper_store_601_rtcu(cpu_env, cpu_gpr[gprn]); -} - -void spr_write_601_rtcl(DisasContext *ctx, int sprn, int gprn) -{ - gen_helper_store_601_rtcl(cpu_env, cpu_gpr[gprn]); -} - -void spr_write_hid0_601(DisasContext *ctx, int sprn, int gprn) -{ - gen_helper_store_hid0_601(cpu_env, cpu_gpr[gprn]); - /* Must stop the translation as endianness may have changed */ - ctx->base.is_jmp = DISAS_EXIT_UPDATE; -} -#endif - -/* Unified bats */ -#if !defined(CONFIG_USER_ONLY) -void spr_read_601_ubat(DisasContext *ctx, int gprn, int sprn) -{ - tcg_gen_ld_tl(cpu_gpr[gprn], cpu_env, - offsetof(CPUPPCState, - IBAT[sprn & 1][(sprn - SPR_IBAT0U) / 2])); -} - -void spr_write_601_ubatu(DisasContext *ctx, int sprn, int gprn) -{ - TCGv_i32 t0 = tcg_const_i32((sprn - SPR_IBAT0U) / 2); - gen_helper_store_601_batl(cpu_env, t0, cpu_gpr[gprn]); - tcg_temp_free_i32(t0); -} - -void spr_write_601_ubatl(DisasContext *ctx, int sprn, int gprn) -{ - TCGv_i32 t0 = tcg_const_i32((sprn - SPR_IBAT0U) / 2); - gen_helper_store_601_batu(cpu_env, t0, cpu_gpr[gprn]); - tcg_temp_free_i32(t0); -} -#endif - /* PowerPC 40x specific registers */ #if !defined(CONFIG_USER_ONLY) void spr_read_40x_pit(DisasContext *ctx, int gprn, int sprn) @@ -5609,669 +5554,6 @@ static void gen_ecowx(DisasContext *ctx) tcg_temp_free(t0); } -/* PowerPC 601 specific instructions */ - -/* abs - abs. */ -static void gen_abs(DisasContext *ctx) -{ - TCGv d = cpu_gpr[rD(ctx->opcode)]; - TCGv a = cpu_gpr[rA(ctx->opcode)]; - - tcg_gen_abs_tl(d, a); - if (unlikely(Rc(ctx->opcode) != 0)) { - gen_set_Rc0(ctx, d); - } -} - -/* abso - abso. */ -static void gen_abso(DisasContext *ctx) -{ - TCGv d = cpu_gpr[rD(ctx->opcode)]; - TCGv a = cpu_gpr[rA(ctx->opcode)]; - - tcg_gen_setcondi_tl(TCG_COND_EQ, cpu_ov, a, 0x80000000); - tcg_gen_abs_tl(d, a); - tcg_gen_or_tl(cpu_so, cpu_so, cpu_ov); - if (unlikely(Rc(ctx->opcode) != 0)) { - gen_set_Rc0(ctx, d); - } -} - -/* clcs */ -static void gen_clcs(DisasContext *ctx) -{ - TCGv_i32 t0 = tcg_const_i32(rA(ctx->opcode)); - gen_helper_clcs(cpu_gpr[rD(ctx->opcode)], cpu_env, t0); - tcg_temp_free_i32(t0); - /* Rc=1 sets CR0 to an undefined state */ -} - -/* div - div. */ -static void gen_div(DisasContext *ctx) -{ - gen_helper_div(cpu_gpr[rD(ctx->opcode)], cpu_env, 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)]); - } -} - -/* divo - divo. */ -static void gen_divo(DisasContext *ctx) -{ - gen_helper_divo(cpu_gpr[rD(ctx->opcode)], cpu_env, 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)]); - } -} - -/* divs - divs. */ -static void gen_divs(DisasContext *ctx) -{ - gen_helper_divs(cpu_gpr[rD(ctx->opcode)], cpu_env, 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)]); - } -} - -/* divso - divso. */ -static void gen_divso(DisasContext *ctx) -{ - gen_helper_divso(cpu_gpr[rD(ctx->opcode)], cpu_env, - 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)]); - } -} - -/* doz - doz. */ -static void gen_doz(DisasContext *ctx) -{ - TCGLabel *l1 = gen_new_label(); - TCGLabel *l2 = gen_new_label(); - tcg_gen_brcond_tl(TCG_COND_GE, cpu_gpr[rB(ctx->opcode)], - cpu_gpr[rA(ctx->opcode)], l1); - tcg_gen_sub_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rB(ctx->opcode)], - cpu_gpr[rA(ctx->opcode)]); - tcg_gen_br(l2); - gen_set_label(l1); - tcg_gen_movi_tl(cpu_gpr[rD(ctx->opcode)], 0); - gen_set_label(l2); - if (unlikely(Rc(ctx->opcode) != 0)) { - gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]); - } -} - -/* dozo - dozo. */ -static void gen_dozo(DisasContext *ctx) -{ - TCGLabel *l1 = gen_new_label(); - TCGLabel *l2 = gen_new_label(); - TCGv t0 = tcg_temp_new(); - TCGv t1 = tcg_temp_new(); - TCGv t2 = tcg_temp_new(); - /* Start with XER OV disabled, the most likely case */ - tcg_gen_movi_tl(cpu_ov, 0); - tcg_gen_brcond_tl(TCG_COND_GE, cpu_gpr[rB(ctx->opcode)], - cpu_gpr[rA(ctx->opcode)], l1); - tcg_gen_sub_tl(t0, cpu_gpr[rB(ctx->opcode)], cpu_gpr[rA(ctx->opcode)]); - tcg_gen_xor_tl(t1, cpu_gpr[rB(ctx->opcode)], cpu_gpr[rA(ctx->opcode)]); - tcg_gen_xor_tl(t2, cpu_gpr[rA(ctx->opcode)], t0); - tcg_gen_andc_tl(t1, t1, t2); - tcg_gen_mov_tl(cpu_gpr[rD(ctx->opcode)], t0); - tcg_gen_brcondi_tl(TCG_COND_GE, t1, 0, l2); - tcg_gen_movi_tl(cpu_ov, 1); - tcg_gen_movi_tl(cpu_so, 1); - tcg_gen_br(l2); - gen_set_label(l1); - tcg_gen_movi_tl(cpu_gpr[rD(ctx->opcode)], 0); - gen_set_label(l2); - tcg_temp_free(t0); - tcg_temp_free(t1); - tcg_temp_free(t2); - if (unlikely(Rc(ctx->opcode) != 0)) { - gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]); - } -} - -/* dozi */ -static void gen_dozi(DisasContext *ctx) -{ - target_long simm = SIMM(ctx->opcode); - TCGLabel *l1 = gen_new_label(); - TCGLabel *l2 = gen_new_label(); - tcg_gen_brcondi_tl(TCG_COND_LT, cpu_gpr[rA(ctx->opcode)], simm, l1); - tcg_gen_subfi_tl(cpu_gpr[rD(ctx->opcode)], simm, cpu_gpr[rA(ctx->opcode)]); - tcg_gen_br(l2); - gen_set_label(l1); - tcg_gen_movi_tl(cpu_gpr[rD(ctx->opcode)], 0); - gen_set_label(l2); - if (unlikely(Rc(ctx->opcode) != 0)) { - gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]); - } -} - -/* lscbx - lscbx. */ -static void gen_lscbx(DisasContext *ctx) -{ - TCGv t0 = tcg_temp_new(); - TCGv_i32 t1 = tcg_const_i32(rD(ctx->opcode)); - TCGv_i32 t2 = tcg_const_i32(rA(ctx->opcode)); - TCGv_i32 t3 = tcg_const_i32(rB(ctx->opcode)); - - gen_addr_reg_index(ctx, t0); - gen_helper_lscbx(t0, cpu_env, t0, t1, t2, t3); - tcg_temp_free_i32(t1); - tcg_temp_free_i32(t2); - tcg_temp_free_i32(t3); - tcg_gen_andi_tl(cpu_xer, cpu_xer, ~0x7F); - tcg_gen_or_tl(cpu_xer, cpu_xer, t0); - if (unlikely(Rc(ctx->opcode) != 0)) { - gen_set_Rc0(ctx, t0); - } - tcg_temp_free(t0); -} - -/* maskg - maskg. */ -static void gen_maskg(DisasContext *ctx) -{ - TCGLabel *l1 = gen_new_label(); - TCGv t0 = tcg_temp_new(); - TCGv t1 = tcg_temp_new(); - TCGv t2 = tcg_temp_new(); - TCGv t3 = tcg_temp_new(); - tcg_gen_movi_tl(t3, 0xFFFFFFFF); - tcg_gen_andi_tl(t0, cpu_gpr[rB(ctx->opcode)], 0x1F); - tcg_gen_andi_tl(t1, cpu_gpr[rS(ctx->opcode)], 0x1F); - tcg_gen_addi_tl(t2, t0, 1); - tcg_gen_shr_tl(t2, t3, t2); - tcg_gen_shr_tl(t3, t3, t1); - tcg_gen_xor_tl(cpu_gpr[rA(ctx->opcode)], t2, t3); - tcg_gen_brcond_tl(TCG_COND_GE, t0, t1, l1); - tcg_gen_neg_tl(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rA(ctx->opcode)]); - gen_set_label(l1); - tcg_temp_free(t0); - tcg_temp_free(t1); - tcg_temp_free(t2); - tcg_temp_free(t3); - if (unlikely(Rc(ctx->opcode) != 0)) { - gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); - } -} - -/* maskir - maskir. */ -static void gen_maskir(DisasContext *ctx) -{ - TCGv t0 = tcg_temp_new(); - TCGv t1 = tcg_temp_new(); - tcg_gen_and_tl(t0, cpu_gpr[rS(ctx->opcode)], cpu_gpr[rB(ctx->opcode)]); - tcg_gen_andc_tl(t1, cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)]); - tcg_gen_or_tl(cpu_gpr[rA(ctx->opcode)], t0, t1); - tcg_temp_free(t0); - tcg_temp_free(t1); - if (unlikely(Rc(ctx->opcode) != 0)) { - gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); - } -} - -/* mul - mul. */ -static void gen_mul(DisasContext *ctx) -{ - TCGv_i64 t0 = tcg_temp_new_i64(); - TCGv_i64 t1 = tcg_temp_new_i64(); - TCGv t2 = tcg_temp_new(); - tcg_gen_extu_tl_i64(t0, cpu_gpr[rA(ctx->opcode)]); - tcg_gen_extu_tl_i64(t1, cpu_gpr[rB(ctx->opcode)]); - tcg_gen_mul_i64(t0, t0, t1); - tcg_gen_trunc_i64_tl(t2, t0); - gen_store_spr(SPR_MQ, t2); - tcg_gen_shri_i64(t1, t0, 32); - tcg_gen_trunc_i64_tl(cpu_gpr[rD(ctx->opcode)], t1); - tcg_temp_free_i64(t0); - tcg_temp_free_i64(t1); - tcg_temp_free(t2); - if (unlikely(Rc(ctx->opcode) != 0)) { - gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]); - } -} - -/* mulo - mulo. */ -static void gen_mulo(DisasContext *ctx) -{ - TCGLabel *l1 = gen_new_label(); - TCGv_i64 t0 = tcg_temp_new_i64(); - TCGv_i64 t1 = tcg_temp_new_i64(); - TCGv t2 = tcg_temp_new(); - /* Start with XER OV disabled, the most likely case */ - tcg_gen_movi_tl(cpu_ov, 0); - tcg_gen_extu_tl_i64(t0, cpu_gpr[rA(ctx->opcode)]); - tcg_gen_extu_tl_i64(t1, cpu_gpr[rB(ctx->opcode)]); - tcg_gen_mul_i64(t0, t0, t1); - tcg_gen_trunc_i64_tl(t2, t0); - gen_store_spr(SPR_MQ, t2); - tcg_gen_shri_i64(t1, t0, 32); - tcg_gen_trunc_i64_tl(cpu_gpr[rD(ctx->opcode)], t1); - tcg_gen_ext32s_i64(t1, t0); - tcg_gen_brcond_i64(TCG_COND_EQ, t0, t1, l1); - tcg_gen_movi_tl(cpu_ov, 1); - tcg_gen_movi_tl(cpu_so, 1); - gen_set_label(l1); - tcg_temp_free_i64(t0); - tcg_temp_free_i64(t1); - tcg_temp_free(t2); - if (unlikely(Rc(ctx->opcode) != 0)) { - gen_set_Rc0(ctx, cpu_gpr[rD(ctx->opcode)]); - } -} - -/* nabs - nabs. */ -static void gen_nabs(DisasContext *ctx) -{ - TCGv d = cpu_gpr[rD(ctx->opcode)]; - TCGv a = cpu_gpr[rA(ctx->opcode)]; - - tcg_gen_abs_tl(d, a); - tcg_gen_neg_tl(d, d); - if (unlikely(Rc(ctx->opcode) != 0)) { - gen_set_Rc0(ctx, d); - } -} - -/* nabso - nabso. */ -static void gen_nabso(DisasContext *ctx) -{ - TCGv d = cpu_gpr[rD(ctx->opcode)]; - TCGv a = cpu_gpr[rA(ctx->opcode)]; - - tcg_gen_abs_tl(d, a); - tcg_gen_neg_tl(d, d); - /* nabs never overflows */ - tcg_gen_movi_tl(cpu_ov, 0); - if (unlikely(Rc(ctx->opcode) != 0)) { - gen_set_Rc0(ctx, d); - } -} - -/* rlmi - rlmi. */ -static void gen_rlmi(DisasContext *ctx) -{ - uint32_t mb = MB(ctx->opcode); - uint32_t me = ME(ctx->opcode); - TCGv t0 = tcg_temp_new(); - tcg_gen_andi_tl(t0, cpu_gpr[rB(ctx->opcode)], 0x1F); - tcg_gen_rotl_tl(t0, cpu_gpr[rS(ctx->opcode)], t0); - tcg_gen_andi_tl(t0, t0, MASK(mb, me)); - tcg_gen_andi_tl(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], - ~MASK(mb, me)); - tcg_gen_or_tl(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], t0); - tcg_temp_free(t0); - if (unlikely(Rc(ctx->opcode) != 0)) { - gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); - } -} - -/* rrib - rrib. */ -static void gen_rrib(DisasContext *ctx) -{ - TCGv t0 = tcg_temp_new(); - TCGv t1 = tcg_temp_new(); - tcg_gen_andi_tl(t0, cpu_gpr[rB(ctx->opcode)], 0x1F); - tcg_gen_movi_tl(t1, 0x80000000); - tcg_gen_shr_tl(t1, t1, t0); - tcg_gen_shr_tl(t0, cpu_gpr[rS(ctx->opcode)], t0); - tcg_gen_and_tl(t0, t0, t1); - tcg_gen_andc_tl(t1, cpu_gpr[rA(ctx->opcode)], t1); - tcg_gen_or_tl(cpu_gpr[rA(ctx->opcode)], t0, t1); - tcg_temp_free(t0); - tcg_temp_free(t1); - if (unlikely(Rc(ctx->opcode) != 0)) { - gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); - } -} - -/* sle - sle. */ -static void gen_sle(DisasContext *ctx) -{ - TCGv t0 = tcg_temp_new(); - TCGv t1 = tcg_temp_new(); - tcg_gen_andi_tl(t1, cpu_gpr[rB(ctx->opcode)], 0x1F); - tcg_gen_shl_tl(t0, cpu_gpr[rS(ctx->opcode)], t1); - tcg_gen_subfi_tl(t1, 32, t1); - tcg_gen_shr_tl(t1, cpu_gpr[rS(ctx->opcode)], t1); - tcg_gen_or_tl(t1, t0, t1); - tcg_gen_mov_tl(cpu_gpr[rA(ctx->opcode)], t0); - gen_store_spr(SPR_MQ, t1); - tcg_temp_free(t0); - tcg_temp_free(t1); - if (unlikely(Rc(ctx->opcode) != 0)) { - gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); - } -} - -/* sleq - sleq. */ -static void gen_sleq(DisasContext *ctx) -{ - TCGv t0 = tcg_temp_new(); - TCGv t1 = tcg_temp_new(); - TCGv t2 = tcg_temp_new(); - tcg_gen_andi_tl(t0, cpu_gpr[rB(ctx->opcode)], 0x1F); - tcg_gen_movi_tl(t2, 0xFFFFFFFF); - tcg_gen_shl_tl(t2, t2, t0); - tcg_gen_rotl_tl(t0, cpu_gpr[rS(ctx->opcode)], t0); - gen_load_spr(t1, SPR_MQ); - gen_store_spr(SPR_MQ, t0); - tcg_gen_and_tl(t0, t0, t2); - tcg_gen_andc_tl(t1, t1, t2); - tcg_gen_or_tl(cpu_gpr[rA(ctx->opcode)], t0, t1); - tcg_temp_free(t0); - tcg_temp_free(t1); - tcg_temp_free(t2); - if (unlikely(Rc(ctx->opcode) != 0)) { - gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); - } -} - -/* sliq - sliq. */ -static void gen_sliq(DisasContext *ctx) -{ - int sh = SH(ctx->opcode); - TCGv t0 = tcg_temp_new(); - TCGv t1 = tcg_temp_new(); - tcg_gen_shli_tl(t0, cpu_gpr[rS(ctx->opcode)], sh); - tcg_gen_shri_tl(t1, cpu_gpr[rS(ctx->opcode)], 32 - sh); - tcg_gen_or_tl(t1, t0, t1); - tcg_gen_mov_tl(cpu_gpr[rA(ctx->opcode)], t0); - gen_store_spr(SPR_MQ, t1); - tcg_temp_free(t0); - tcg_temp_free(t1); - if (unlikely(Rc(ctx->opcode) != 0)) { - gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); - } -} - -/* slliq - slliq. */ -static void gen_slliq(DisasContext *ctx) -{ - int sh = SH(ctx->opcode); - TCGv t0 = tcg_temp_new(); - TCGv t1 = tcg_temp_new(); - tcg_gen_rotli_tl(t0, cpu_gpr[rS(ctx->opcode)], sh); - gen_load_spr(t1, SPR_MQ); - gen_store_spr(SPR_MQ, t0); - tcg_gen_andi_tl(t0, t0, (0xFFFFFFFFU << sh)); - tcg_gen_andi_tl(t1, t1, ~(0xFFFFFFFFU << sh)); - tcg_gen_or_tl(cpu_gpr[rA(ctx->opcode)], t0, t1); - tcg_temp_free(t0); - tcg_temp_free(t1); - if (unlikely(Rc(ctx->opcode) != 0)) { - gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); - } -} - -/* sllq - sllq. */ -static void gen_sllq(DisasContext *ctx) -{ - TCGLabel *l1 = gen_new_label(); - TCGLabel *l2 = gen_new_label(); - TCGv t0 = tcg_temp_local_new(); - TCGv t1 = tcg_temp_local_new(); - TCGv t2 = tcg_temp_local_new(); - tcg_gen_andi_tl(t2, cpu_gpr[rB(ctx->opcode)], 0x1F); - tcg_gen_movi_tl(t1, 0xFFFFFFFF); - tcg_gen_shl_tl(t1, t1, t2); - tcg_gen_andi_tl(t0, cpu_gpr[rB(ctx->opcode)], 0x20); - tcg_gen_brcondi_tl(TCG_COND_EQ, t0, 0, l1); - gen_load_spr(t0, SPR_MQ); - tcg_gen_and_tl(cpu_gpr[rA(ctx->opcode)], t0, t1); - tcg_gen_br(l2); - gen_set_label(l1); - tcg_gen_shl_tl(t0, cpu_gpr[rS(ctx->opcode)], t2); - gen_load_spr(t2, SPR_MQ); - tcg_gen_andc_tl(t1, t2, t1); - tcg_gen_or_tl(cpu_gpr[rA(ctx->opcode)], t0, t1); - gen_set_label(l2); - tcg_temp_free(t0); - tcg_temp_free(t1); - tcg_temp_free(t2); - if (unlikely(Rc(ctx->opcode) != 0)) { - gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); - } -} - -/* slq - slq. */ -static void gen_slq(DisasContext *ctx) -{ - TCGLabel *l1 = gen_new_label(); - TCGv t0 = tcg_temp_new(); - TCGv t1 = tcg_temp_new(); - tcg_gen_andi_tl(t1, cpu_gpr[rB(ctx->opcode)], 0x1F); - tcg_gen_shl_tl(t0, cpu_gpr[rS(ctx->opcode)], t1); - tcg_gen_subfi_tl(t1, 32, t1); - tcg_gen_shr_tl(t1, cpu_gpr[rS(ctx->opcode)], t1); - tcg_gen_or_tl(t1, t0, t1); - gen_store_spr(SPR_MQ, t1); - tcg_gen_andi_tl(t1, cpu_gpr[rB(ctx->opcode)], 0x20); - tcg_gen_mov_tl(cpu_gpr[rA(ctx->opcode)], t0); - tcg_gen_brcondi_tl(TCG_COND_EQ, t1, 0, l1); - tcg_gen_movi_tl(cpu_gpr[rA(ctx->opcode)], 0); - gen_set_label(l1); - tcg_temp_free(t0); - tcg_temp_free(t1); - if (unlikely(Rc(ctx->opcode) != 0)) { - gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); - } -} - -/* sraiq - sraiq. */ -static void gen_sraiq(DisasContext *ctx) -{ - int sh = SH(ctx->opcode); - TCGLabel *l1 = gen_new_label(); - TCGv t0 = tcg_temp_new(); - TCGv t1 = tcg_temp_new(); - tcg_gen_shri_tl(t0, cpu_gpr[rS(ctx->opcode)], sh); - tcg_gen_shli_tl(t1, cpu_gpr[rS(ctx->opcode)], 32 - sh); - tcg_gen_or_tl(t0, t0, t1); - gen_store_spr(SPR_MQ, t0); - tcg_gen_movi_tl(cpu_ca, 0); - tcg_gen_brcondi_tl(TCG_COND_EQ, t1, 0, l1); - tcg_gen_brcondi_tl(TCG_COND_GE, cpu_gpr[rS(ctx->opcode)], 0, l1); - tcg_gen_movi_tl(cpu_ca, 1); - gen_set_label(l1); - tcg_gen_sari_tl(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)], sh); - tcg_temp_free(t0); - tcg_temp_free(t1); - if (unlikely(Rc(ctx->opcode) != 0)) { - gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); - } -} - -/* sraq - sraq. */ -static void gen_sraq(DisasContext *ctx) -{ - TCGLabel *l1 = gen_new_label(); - TCGLabel *l2 = gen_new_label(); - TCGv t0 = tcg_temp_new(); - TCGv t1 = tcg_temp_local_new(); - TCGv t2 = tcg_temp_local_new(); - tcg_gen_andi_tl(t2, cpu_gpr[rB(ctx->opcode)], 0x1F); - tcg_gen_shr_tl(t0, cpu_gpr[rS(ctx->opcode)], t2); - tcg_gen_sar_tl(t1, cpu_gpr[rS(ctx->opcode)], t2); - tcg_gen_subfi_tl(t2, 32, t2); - tcg_gen_shl_tl(t2, cpu_gpr[rS(ctx->opcode)], t2); - tcg_gen_or_tl(t0, t0, t2); - gen_store_spr(SPR_MQ, t0); - tcg_gen_andi_tl(t0, cpu_gpr[rB(ctx->opcode)], 0x20); - tcg_gen_brcondi_tl(TCG_COND_EQ, t2, 0, l1); - tcg_gen_mov_tl(t2, cpu_gpr[rS(ctx->opcode)]); - tcg_gen_sari_tl(t1, cpu_gpr[rS(ctx->opcode)], 31); - gen_set_label(l1); - tcg_temp_free(t0); - tcg_gen_mov_tl(cpu_gpr[rA(ctx->opcode)], t1); - tcg_gen_movi_tl(cpu_ca, 0); - tcg_gen_brcondi_tl(TCG_COND_GE, t1, 0, l2); - tcg_gen_brcondi_tl(TCG_COND_EQ, t2, 0, l2); - tcg_gen_movi_tl(cpu_ca, 1); - gen_set_label(l2); - tcg_temp_free(t1); - tcg_temp_free(t2); - if (unlikely(Rc(ctx->opcode) != 0)) { - gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); - } -} - -/* sre - sre. */ -static void gen_sre(DisasContext *ctx) -{ - TCGv t0 = tcg_temp_new(); - TCGv t1 = tcg_temp_new(); - tcg_gen_andi_tl(t1, cpu_gpr[rB(ctx->opcode)], 0x1F); - tcg_gen_shr_tl(t0, cpu_gpr[rS(ctx->opcode)], t1); - tcg_gen_subfi_tl(t1, 32, t1); - tcg_gen_shl_tl(t1, cpu_gpr[rS(ctx->opcode)], t1); - tcg_gen_or_tl(t1, t0, t1); - tcg_gen_mov_tl(cpu_gpr[rA(ctx->opcode)], t0); - gen_store_spr(SPR_MQ, t1); - tcg_temp_free(t0); - tcg_temp_free(t1); - if (unlikely(Rc(ctx->opcode) != 0)) { - gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); - } -} - -/* srea - srea. */ -static void gen_srea(DisasContext *ctx) -{ - TCGv t0 = tcg_temp_new(); - TCGv t1 = tcg_temp_new(); - tcg_gen_andi_tl(t1, cpu_gpr[rB(ctx->opcode)], 0x1F); - tcg_gen_rotr_tl(t0, cpu_gpr[rS(ctx->opcode)], t1); - gen_store_spr(SPR_MQ, t0); - tcg_gen_sar_tl(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rS(ctx->opcode)], t1); - tcg_temp_free(t0); - tcg_temp_free(t1); - if (unlikely(Rc(ctx->opcode) != 0)) { - gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); - } -} - -/* sreq */ -static void gen_sreq(DisasContext *ctx) -{ - TCGv t0 = tcg_temp_new(); - TCGv t1 = tcg_temp_new(); - TCGv t2 = tcg_temp_new(); - tcg_gen_andi_tl(t0, cpu_gpr[rB(ctx->opcode)], 0x1F); - tcg_gen_movi_tl(t1, 0xFFFFFFFF); - tcg_gen_shr_tl(t1, t1, t0); - tcg_gen_rotr_tl(t0, cpu_gpr[rS(ctx->opcode)], t0); - gen_load_spr(t2, SPR_MQ); - gen_store_spr(SPR_MQ, t0); - tcg_gen_and_tl(t0, t0, t1); - tcg_gen_andc_tl(t2, t2, t1); - tcg_gen_or_tl(cpu_gpr[rA(ctx->opcode)], t0, t2); - tcg_temp_free(t0); - tcg_temp_free(t1); - tcg_temp_free(t2); - if (unlikely(Rc(ctx->opcode) != 0)) { - gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); - } -} - -/* sriq */ -static void gen_sriq(DisasContext *ctx) -{ - int sh = SH(ctx->opcode); - TCGv t0 = tcg_temp_new(); - TCGv t1 = tcg_temp_new(); - tcg_gen_shri_tl(t0, cpu_gpr[rS(ctx->opcode)], sh); - tcg_gen_shli_tl(t1, cpu_gpr[rS(ctx->opcode)], 32 - sh); - tcg_gen_or_tl(t1, t0, t1); - tcg_gen_mov_tl(cpu_gpr[rA(ctx->opcode)], t0); - gen_store_spr(SPR_MQ, t1); - tcg_temp_free(t0); - tcg_temp_free(t1); - if (unlikely(Rc(ctx->opcode) != 0)) { - gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); - } -} - -/* srliq */ -static void gen_srliq(DisasContext *ctx) -{ - int sh = SH(ctx->opcode); - TCGv t0 = tcg_temp_new(); - TCGv t1 = tcg_temp_new(); - tcg_gen_rotri_tl(t0, cpu_gpr[rS(ctx->opcode)], sh); - gen_load_spr(t1, SPR_MQ); - gen_store_spr(SPR_MQ, t0); - tcg_gen_andi_tl(t0, t0, (0xFFFFFFFFU >> sh)); - tcg_gen_andi_tl(t1, t1, ~(0xFFFFFFFFU >> sh)); - tcg_gen_or_tl(cpu_gpr[rA(ctx->opcode)], t0, t1); - tcg_temp_free(t0); - tcg_temp_free(t1); - if (unlikely(Rc(ctx->opcode) != 0)) { - gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); - } -} - -/* srlq */ -static void gen_srlq(DisasContext *ctx) -{ - TCGLabel *l1 = gen_new_label(); - TCGLabel *l2 = gen_new_label(); - TCGv t0 = tcg_temp_local_new(); - TCGv t1 = tcg_temp_local_new(); - TCGv t2 = tcg_temp_local_new(); - tcg_gen_andi_tl(t2, cpu_gpr[rB(ctx->opcode)], 0x1F); - tcg_gen_movi_tl(t1, 0xFFFFFFFF); - tcg_gen_shr_tl(t2, t1, t2); - tcg_gen_andi_tl(t0, cpu_gpr[rB(ctx->opcode)], 0x20); - tcg_gen_brcondi_tl(TCG_COND_EQ, t0, 0, l1); - gen_load_spr(t0, SPR_MQ); - tcg_gen_and_tl(cpu_gpr[rA(ctx->opcode)], t0, t2); - tcg_gen_br(l2); - gen_set_label(l1); - tcg_gen_shr_tl(t0, cpu_gpr[rS(ctx->opcode)], t2); - tcg_gen_and_tl(t0, t0, t2); - gen_load_spr(t1, SPR_MQ); - tcg_gen_andc_tl(t1, t1, t2); - tcg_gen_or_tl(cpu_gpr[rA(ctx->opcode)], t0, t1); - gen_set_label(l2); - tcg_temp_free(t0); - tcg_temp_free(t1); - tcg_temp_free(t2); - if (unlikely(Rc(ctx->opcode) != 0)) { - gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); - } -} - -/* srq */ -static void gen_srq(DisasContext *ctx) -{ - TCGLabel *l1 = gen_new_label(); - TCGv t0 = tcg_temp_new(); - TCGv t1 = tcg_temp_new(); - tcg_gen_andi_tl(t1, cpu_gpr[rB(ctx->opcode)], 0x1F); - tcg_gen_shr_tl(t0, cpu_gpr[rS(ctx->opcode)], t1); - tcg_gen_subfi_tl(t1, 32, t1); - tcg_gen_shl_tl(t1, cpu_gpr[rS(ctx->opcode)], t1); - tcg_gen_or_tl(t1, t0, t1); - gen_store_spr(SPR_MQ, t1); - tcg_gen_andi_tl(t1, cpu_gpr[rB(ctx->opcode)], 0x20); - tcg_gen_mov_tl(cpu_gpr[rA(ctx->opcode)], t0); - tcg_gen_brcondi_tl(TCG_COND_EQ, t0, 0, l1); - tcg_gen_movi_tl(cpu_gpr[rA(ctx->opcode)], 0); - gen_set_label(l1); - tcg_temp_free(t0); - tcg_temp_free(t1); - if (unlikely(Rc(ctx->opcode) != 0)) { - gen_set_Rc0(ctx, cpu_gpr[rA(ctx->opcode)]); - } -} - /* 602 - 603 - G2 TLB management */ /* tlbld */ @@ -6296,81 +5578,6 @@ static void gen_tlbli_6xx(DisasContext *ctx) #endif /* defined(CONFIG_USER_ONLY) */ } -/* POWER instructions not in PowerPC 601 */ - -/* clf */ -static void gen_clf(DisasContext *ctx) -{ - /* Cache line flush: implemented as no-op */ -} - -/* cli */ -static void gen_cli(DisasContext *ctx) -{ -#if defined(CONFIG_USER_ONLY) - GEN_PRIV; -#else - /* Cache line invalidate: privileged and treated as no-op */ - CHK_SV; -#endif /* defined(CONFIG_USER_ONLY) */ -} - -/* dclst */ -static void gen_dclst(DisasContext *ctx) -{ - /* Data cache line store: treated as no-op */ -} - -static void gen_mfsri(DisasContext *ctx) -{ -#if defined(CONFIG_USER_ONLY) - GEN_PRIV; -#else - int ra = rA(ctx->opcode); - int rd = rD(ctx->opcode); - TCGv t0; - - CHK_SV; - t0 = tcg_temp_new(); - gen_addr_reg_index(ctx, t0); - tcg_gen_extract_tl(t0, t0, 28, 4); - gen_helper_load_sr(cpu_gpr[rd], cpu_env, t0); - tcg_temp_free(t0); - if (ra != 0 && ra != rd) { - tcg_gen_mov_tl(cpu_gpr[ra], cpu_gpr[rd]); - } -#endif /* defined(CONFIG_USER_ONLY) */ -} - -static void gen_rac(DisasContext *ctx) -{ -#if defined(CONFIG_USER_ONLY) - GEN_PRIV; -#else - TCGv t0; - - CHK_SV; - t0 = tcg_temp_new(); - gen_addr_reg_index(ctx, t0); - gen_helper_rac(cpu_gpr[rD(ctx->opcode)], cpu_env, t0); - tcg_temp_free(t0); -#endif /* defined(CONFIG_USER_ONLY) */ -} - -static void gen_rfsvc(DisasContext *ctx) -{ -#if defined(CONFIG_USER_ONLY) - GEN_PRIV; -#else - CHK_SV; - - gen_helper_rfsvc(cpu_env); - ctx->base.is_jmp = DISAS_EXIT; -#endif /* defined(CONFIG_USER_ONLY) */ -} - -/* svc is not implemented for now */ - /* BookE specific instructions */ /* XXX: not implemented on 440 ? */ @@ -7718,56 +6925,8 @@ GEN_HANDLER_E(slbsync, 0x1F, 0x12, 0x0A, 0x03FFF801, PPC_NONE, PPC2_ISA300), #endif GEN_HANDLER(eciwx, 0x1F, 0x16, 0x0D, 0x00000001, PPC_EXTERN), GEN_HANDLER(ecowx, 0x1F, 0x16, 0x09, 0x00000001, PPC_EXTERN), -GEN_HANDLER(abs, 0x1F, 0x08, 0x0B, 0x0000F800, PPC_POWER_BR), -GEN_HANDLER(abso, 0x1F, 0x08, 0x1B, 0x0000F800, PPC_POWER_BR), -GEN_HANDLER(clcs, 0x1F, 0x10, 0x13, 0x0000F800, PPC_POWER_BR), -GEN_HANDLER(div, 0x1F, 0x0B, 0x0A, 0x00000000, PPC_POWER_BR), -GEN_HANDLER(divo, 0x1F, 0x0B, 0x1A, 0x00000000, PPC_POWER_BR), -GEN_HANDLER(divs, 0x1F, 0x0B, 0x0B, 0x00000000, PPC_POWER_BR), -GEN_HANDLER(divso, 0x1F, 0x0B, 0x1B, 0x00000000, PPC_POWER_BR), -GEN_HANDLER(doz, 0x1F, 0x08, 0x08, 0x00000000, PPC_POWER_BR), -GEN_HANDLER(dozo, 0x1F, 0x08, 0x18, 0x00000000, PPC_POWER_BR), -GEN_HANDLER(dozi, 0x09, 0xFF, 0xFF, 0x00000000, PPC_POWER_BR), -GEN_HANDLER(lscbx, 0x1F, 0x15, 0x08, 0x00000000, PPC_POWER_BR), -GEN_HANDLER(maskg, 0x1F, 0x1D, 0x00, 0x00000000, PPC_POWER_BR), -GEN_HANDLER(maskir, 0x1F, 0x1D, 0x10, 0x00000000, PPC_POWER_BR), -GEN_HANDLER(mul, 0x1F, 0x0B, 0x03, 0x00000000, PPC_POWER_BR), -GEN_HANDLER(mulo, 0x1F, 0x0B, 0x13, 0x00000000, PPC_POWER_BR), -GEN_HANDLER(nabs, 0x1F, 0x08, 0x0F, 0x00000000, PPC_POWER_BR), -GEN_HANDLER(nabso, 0x1F, 0x08, 0x1F, 0x00000000, PPC_POWER_BR), -GEN_HANDLER(rlmi, 0x16, 0xFF, 0xFF, 0x00000000, PPC_POWER_BR), -GEN_HANDLER(rrib, 0x1F, 0x19, 0x10, 0x00000000, PPC_POWER_BR), -GEN_HANDLER(sle, 0x1F, 0x19, 0x04, 0x00000000, PPC_POWER_BR), -GEN_HANDLER(sleq, 0x1F, 0x19, 0x06, 0x00000000, PPC_POWER_BR), -GEN_HANDLER(sliq, 0x1F, 0x18, 0x05, 0x00000000, PPC_POWER_BR), -GEN_HANDLER(slliq, 0x1F, 0x18, 0x07, 0x00000000, PPC_POWER_BR), -GEN_HANDLER(sllq, 0x1F, 0x18, 0x06, 0x00000000, PPC_POWER_BR), -GEN_HANDLER(slq, 0x1F, 0x18, 0x04, 0x00000000, PPC_POWER_BR), -GEN_HANDLER(sraiq, 0x1F, 0x18, 0x1D, 0x00000000, PPC_POWER_BR), -GEN_HANDLER(sraq, 0x1F, 0x18, 0x1C, 0x00000000, PPC_POWER_BR), -GEN_HANDLER(sre, 0x1F, 0x19, 0x14, 0x00000000, PPC_POWER_BR), -GEN_HANDLER(srea, 0x1F, 0x19, 0x1C, 0x00000000, PPC_POWER_BR), -GEN_HANDLER(sreq, 0x1F, 0x19, 0x16, 0x00000000, PPC_POWER_BR), -GEN_HANDLER(sriq, 0x1F, 0x18, 0x15, 0x00000000, PPC_POWER_BR), -GEN_HANDLER(srliq, 0x1F, 0x18, 0x17, 0x00000000, PPC_POWER_BR), -GEN_HANDLER(srlq, 0x1F, 0x18, 0x16, 0x00000000, PPC_POWER_BR), -GEN_HANDLER(srq, 0x1F, 0x18, 0x14, 0x00000000, PPC_POWER_BR), GEN_HANDLER2(tlbld_6xx, "tlbld", 0x1F, 0x12, 0x1E, 0x03FF0001, PPC_6xx_TLB), GEN_HANDLER2(tlbli_6xx, "tlbli", 0x1F, 0x12, 0x1F, 0x03FF0001, PPC_6xx_TLB), -GEN_HANDLER(clf, 0x1F, 0x16, 0x03, 0x03E00000, PPC_POWER), -GEN_HANDLER(cli, 0x1F, 0x16, 0x0F, 0x03E00000, PPC_POWER), -GEN_HANDLER(dclst, 0x1F, 0x16, 0x13, 0x03E00000, PPC_POWER), -GEN_HANDLER(mfsri, 0x1F, 0x13, 0x13, 0x00000001, PPC_POWER), -GEN_HANDLER(rac, 0x1F, 0x12, 0x19, 0x00000001, PPC_POWER), -GEN_HANDLER(rfsvc, 0x13, 0x12, 0x02, 0x03FFF0001, PPC_POWER), -GEN_HANDLER(lfq, 0x38, 0xFF, 0xFF, 0x00000003, PPC_POWER2), -GEN_HANDLER(lfqu, 0x39, 0xFF, 0xFF, 0x00000003, PPC_POWER2), -GEN_HANDLER(lfqux, 0x1F, 0x17, 0x19, 0x00000001, PPC_POWER2), -GEN_HANDLER(lfqx, 0x1F, 0x17, 0x18, 0x00000001, PPC_POWER2), -GEN_HANDLER(stfq, 0x3C, 0xFF, 0xFF, 0x00000003, PPC_POWER2), -GEN_HANDLER(stfqu, 0x3D, 0xFF, 0xFF, 0x00000003, PPC_POWER2), -GEN_HANDLER(stfqux, 0x1F, 0x17, 0x1D, 0x00000001, PPC_POWER2), -GEN_HANDLER(stfqx, 0x1F, 0x17, 0x1C, 0x00000001, PPC_POWER2), GEN_HANDLER(mfapidi, 0x1F, 0x13, 0x08, 0x0000F801, PPC_MFAPIDI), GEN_HANDLER(tlbiva, 0x1F, 0x12, 0x18, 0x03FFF801, PPC_TLBIVA), GEN_HANDLER(mfdcr, 0x1F, 0x03, 0x0A, 0x00000001, PPC_DCR), @@ -8463,7 +7622,6 @@ static void ppc_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) ctx->has_cfar = !!(env->flags & POWERPC_FLAG_CFAR); #endif ctx->lazy_tlb_flush = env->mmu_model == POWERPC_MMU_32B - || env->mmu_model == POWERPC_MMU_601 || env->mmu_model & POWERPC_MMU_64; ctx->fpu_enabled = (hflags >> HFLAGS_FP) & 1; diff --git a/target/ppc/translate/fp-impl.c.inc b/target/ppc/translate/fp-impl.c.inc index c96769742e..cfb27bd020 100644 --- a/target/ppc/translate/fp-impl.c.inc +++ b/target/ppc/translate/fp-impl.c.inc @@ -1105,185 +1105,6 @@ static inline void gen_qemu_st32fiw(DisasContext *ctx, TCGv_i64 arg1, TCGv arg2) /* stfiwx */ GEN_STXF(stfiw, st32fiw, 0x17, 0x1E, PPC_FLOAT_STFIWX); -/* POWER2 specific instructions */ -/* Quad manipulation (load/store two floats at a time) */ - -/* lfq */ -static void gen_lfq(DisasContext *ctx) -{ - int rd = rD(ctx->opcode); - TCGv t0; - TCGv_i64 t1; - gen_set_access_type(ctx, ACCESS_FLOAT); - t0 = tcg_temp_new(); - t1 = tcg_temp_new_i64(); - gen_addr_imm_index(ctx, t0, 0); - gen_qemu_ld64_i64(ctx, t1, t0); - set_fpr(rd, t1); - gen_addr_add(ctx, t0, t0, 8); - gen_qemu_ld64_i64(ctx, t1, t0); - set_fpr((rd + 1) % 32, t1); - tcg_temp_free(t0); - tcg_temp_free_i64(t1); -} - -/* lfqu */ -static void gen_lfqu(DisasContext *ctx) -{ - int ra = rA(ctx->opcode); - int rd = rD(ctx->opcode); - TCGv t0, t1; - TCGv_i64 t2; - gen_set_access_type(ctx, ACCESS_FLOAT); - t0 = tcg_temp_new(); - t1 = tcg_temp_new(); - t2 = tcg_temp_new_i64(); - gen_addr_imm_index(ctx, t0, 0); - gen_qemu_ld64_i64(ctx, t2, t0); - set_fpr(rd, t2); - gen_addr_add(ctx, t1, t0, 8); - gen_qemu_ld64_i64(ctx, t2, t1); - set_fpr((rd + 1) % 32, t2); - if (ra != 0) { - tcg_gen_mov_tl(cpu_gpr[ra], t0); - } - tcg_temp_free(t0); - tcg_temp_free(t1); - tcg_temp_free_i64(t2); -} - -/* lfqux */ -static void gen_lfqux(DisasContext *ctx) -{ - int ra = rA(ctx->opcode); - int rd = rD(ctx->opcode); - gen_set_access_type(ctx, ACCESS_FLOAT); - TCGv t0, t1; - TCGv_i64 t2; - t2 = tcg_temp_new_i64(); - t0 = tcg_temp_new(); - gen_addr_reg_index(ctx, t0); - gen_qemu_ld64_i64(ctx, t2, t0); - set_fpr(rd, t2); - t1 = tcg_temp_new(); - gen_addr_add(ctx, t1, t0, 8); - gen_qemu_ld64_i64(ctx, t2, t1); - set_fpr((rd + 1) % 32, t2); - tcg_temp_free(t1); - if (ra != 0) { - tcg_gen_mov_tl(cpu_gpr[ra], t0); - } - tcg_temp_free(t0); - tcg_temp_free_i64(t2); -} - -/* lfqx */ -static void gen_lfqx(DisasContext *ctx) -{ - int rd = rD(ctx->opcode); - TCGv t0; - TCGv_i64 t1; - gen_set_access_type(ctx, ACCESS_FLOAT); - t0 = tcg_temp_new(); - t1 = tcg_temp_new_i64(); - gen_addr_reg_index(ctx, t0); - gen_qemu_ld64_i64(ctx, t1, t0); - set_fpr(rd, t1); - gen_addr_add(ctx, t0, t0, 8); - gen_qemu_ld64_i64(ctx, t1, t0); - set_fpr((rd + 1) % 32, t1); - tcg_temp_free(t0); - tcg_temp_free_i64(t1); -} - -/* stfq */ -static void gen_stfq(DisasContext *ctx) -{ - int rd = rD(ctx->opcode); - TCGv t0; - TCGv_i64 t1; - gen_set_access_type(ctx, ACCESS_FLOAT); - t0 = tcg_temp_new(); - t1 = tcg_temp_new_i64(); - gen_addr_imm_index(ctx, t0, 0); - get_fpr(t1, rd); - gen_qemu_st64_i64(ctx, t1, t0); - gen_addr_add(ctx, t0, t0, 8); - get_fpr(t1, (rd + 1) % 32); - gen_qemu_st64_i64(ctx, t1, t0); - tcg_temp_free(t0); - tcg_temp_free_i64(t1); -} - -/* stfqu */ -static void gen_stfqu(DisasContext *ctx) -{ - int ra = rA(ctx->opcode); - int rd = rD(ctx->opcode); - TCGv t0, t1; - TCGv_i64 t2; - gen_set_access_type(ctx, ACCESS_FLOAT); - t2 = tcg_temp_new_i64(); - t0 = tcg_temp_new(); - gen_addr_imm_index(ctx, t0, 0); - get_fpr(t2, rd); - gen_qemu_st64_i64(ctx, t2, t0); - t1 = tcg_temp_new(); - gen_addr_add(ctx, t1, t0, 8); - get_fpr(t2, (rd + 1) % 32); - gen_qemu_st64_i64(ctx, t2, t1); - tcg_temp_free(t1); - if (ra != 0) { - tcg_gen_mov_tl(cpu_gpr[ra], t0); - } - tcg_temp_free(t0); - tcg_temp_free_i64(t2); -} - -/* stfqux */ -static void gen_stfqux(DisasContext *ctx) -{ - int ra = rA(ctx->opcode); - int rd = rD(ctx->opcode); - TCGv t0, t1; - TCGv_i64 t2; - gen_set_access_type(ctx, ACCESS_FLOAT); - t2 = tcg_temp_new_i64(); - t0 = tcg_temp_new(); - gen_addr_reg_index(ctx, t0); - get_fpr(t2, rd); - gen_qemu_st64_i64(ctx, t2, t0); - t1 = tcg_temp_new(); - gen_addr_add(ctx, t1, t0, 8); - get_fpr(t2, (rd + 1) % 32); - gen_qemu_st64_i64(ctx, t2, t1); - tcg_temp_free(t1); - if (ra != 0) { - tcg_gen_mov_tl(cpu_gpr[ra], t0); - } - tcg_temp_free(t0); - tcg_temp_free_i64(t2); -} - -/* stfqx */ -static void gen_stfqx(DisasContext *ctx) -{ - int rd = rD(ctx->opcode); - TCGv t0; - TCGv_i64 t1; - gen_set_access_type(ctx, ACCESS_FLOAT); - t1 = tcg_temp_new_i64(); - t0 = tcg_temp_new(); - gen_addr_reg_index(ctx, t0); - get_fpr(t1, rd); - gen_qemu_st64_i64(ctx, t1, t0); - gen_addr_add(ctx, t0, t0, 8); - get_fpr(t1, (rd + 1) % 32); - gen_qemu_st64_i64(ctx, t1, t0); - tcg_temp_free(t0); - tcg_temp_free_i64(t1); -} - /* Floating-point Load/Store Instructions */ static bool do_lsfpsd(DisasContext *ctx, int rt, int ra, TCGv displ, bool update, bool store, bool single) From 9323650f973272c065ea28c8f2864cc30aecc665 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Wed, 9 Feb 2022 09:08:55 +0100 Subject: [PATCH 343/460] target/ppc: Merge exception model IDs for 6xx CPUs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We don't need three separate exception model IDs for the 603, 604 and G2. Signed-off-by: Fabiano Rosas Message-Id: <20220203200957.1434641-2-farosas@linux.ibm.com> Signed-off-by: Cédric Le Goater --- target/ppc/cpu-qom.h | 8 ++------ target/ppc/cpu_init.c | 18 +++++++++--------- target/ppc/excp_helper.c | 5 ++--- 3 files changed, 13 insertions(+), 18 deletions(-) diff --git a/target/ppc/cpu-qom.h b/target/ppc/cpu-qom.h index 5d591ff6c5..3880fb3337 100644 --- a/target/ppc/cpu-qom.h +++ b/target/ppc/cpu-qom.h @@ -88,12 +88,8 @@ enum powerpc_excp_t { POWERPC_EXCP_STD, /* PowerPC 40x exception model */ POWERPC_EXCP_40x, - /* PowerPC 603 exception model */ - POWERPC_EXCP_603, - /* PowerPC G2 exception model */ - POWERPC_EXCP_G2, - /* PowerPC 604 exception model */ - POWERPC_EXCP_604, + /* PowerPC 603/604/G2 exception model */ + POWERPC_EXCP_6xx, /* PowerPC 7x0 exception model */ POWERPC_EXCP_7x0, /* PowerPC 7x5 exception model */ diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index a5e1f5a3b2..95c5831ba6 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -2782,7 +2782,7 @@ POWERPC_FAMILY(MPC5xx)(ObjectClass *oc, void *data) (1ull << MSR_RI) | (1ull << MSR_LE); pcc->mmu_model = POWERPC_MMU_REAL; - pcc->excp_model = POWERPC_EXCP_603; + pcc->excp_model = POWERPC_EXCP_6xx; pcc->bus_model = PPC_FLAGS_INPUT_RCPU; pcc->bfd_mach = bfd_mach_ppc_505; pcc->flags = POWERPC_FLAG_SE | POWERPC_FLAG_BE | @@ -2825,7 +2825,7 @@ POWERPC_FAMILY(MPC8xx)(ObjectClass *oc, void *data) (1ull << MSR_RI) | (1ull << MSR_LE); pcc->mmu_model = POWERPC_MMU_MPC8xx; - pcc->excp_model = POWERPC_EXCP_603; + pcc->excp_model = POWERPC_EXCP_6xx; pcc->bus_model = PPC_FLAGS_INPUT_RCPU; pcc->bfd_mach = bfd_mach_ppc_860; pcc->flags = POWERPC_FLAG_SE | POWERPC_FLAG_BE | @@ -2906,7 +2906,7 @@ POWERPC_FAMILY(G2)(ObjectClass *oc, void *data) (1ull << MSR_DR) | (1ull << MSR_RI); pcc->mmu_model = POWERPC_MMU_SOFT_6xx; - pcc->excp_model = POWERPC_EXCP_G2; + pcc->excp_model = POWERPC_EXCP_6xx; pcc->bus_model = PPC_FLAGS_INPUT_6xx; pcc->bfd_mach = bfd_mach_ppc_ec603e; pcc->flags = POWERPC_FLAG_TGPR | POWERPC_FLAG_SE | @@ -2988,7 +2988,7 @@ POWERPC_FAMILY(G2LE)(ObjectClass *oc, void *data) (1ull << MSR_RI) | (1ull << MSR_LE); pcc->mmu_model = POWERPC_MMU_SOFT_6xx; - pcc->excp_model = POWERPC_EXCP_G2; + pcc->excp_model = POWERPC_EXCP_6xx; pcc->bus_model = PPC_FLAGS_INPUT_6xx; pcc->bfd_mach = bfd_mach_ppc_ec603e; pcc->flags = POWERPC_FLAG_TGPR | POWERPC_FLAG_SE | @@ -3243,7 +3243,7 @@ POWERPC_FAMILY(e300)(ObjectClass *oc, void *data) (1ull << MSR_RI) | (1ull << MSR_LE); pcc->mmu_model = POWERPC_MMU_SOFT_6xx; - pcc->excp_model = POWERPC_EXCP_603; + pcc->excp_model = POWERPC_EXCP_6xx; pcc->bus_model = PPC_FLAGS_INPUT_6xx; pcc->bfd_mach = bfd_mach_ppc_603; pcc->flags = POWERPC_FLAG_TGPR | POWERPC_FLAG_SE | @@ -3772,7 +3772,7 @@ POWERPC_FAMILY(603)(ObjectClass *oc, void *data) (1ull << MSR_RI) | (1ull << MSR_LE); pcc->mmu_model = POWERPC_MMU_SOFT_6xx; - pcc->excp_model = POWERPC_EXCP_603; + pcc->excp_model = POWERPC_EXCP_6xx; pcc->bus_model = PPC_FLAGS_INPUT_6xx; pcc->bfd_mach = bfd_mach_ppc_603; pcc->flags = POWERPC_FLAG_TGPR | POWERPC_FLAG_SE | @@ -3811,7 +3811,7 @@ POWERPC_FAMILY(603E)(ObjectClass *oc, void *data) (1ull << MSR_RI) | (1ull << MSR_LE); pcc->mmu_model = POWERPC_MMU_SOFT_6xx; - pcc->excp_model = POWERPC_EXCP_603; + pcc->excp_model = POWERPC_EXCP_6xx; pcc->bus_model = PPC_FLAGS_INPUT_6xx; pcc->bfd_mach = bfd_mach_ppc_ec603e; pcc->flags = POWERPC_FLAG_TGPR | POWERPC_FLAG_SE | @@ -3872,7 +3872,7 @@ POWERPC_FAMILY(604)(ObjectClass *oc, void *data) (1ull << MSR_RI) | (1ull << MSR_LE); pcc->mmu_model = POWERPC_MMU_32B; - pcc->excp_model = POWERPC_EXCP_604; + pcc->excp_model = POWERPC_EXCP_6xx; pcc->bus_model = PPC_FLAGS_INPUT_6xx; pcc->bfd_mach = bfd_mach_ppc_604; pcc->flags = POWERPC_FLAG_SE | POWERPC_FLAG_BE | @@ -3953,7 +3953,7 @@ POWERPC_FAMILY(604E)(ObjectClass *oc, void *data) (1ull << MSR_RI) | (1ull << MSR_LE); pcc->mmu_model = POWERPC_MMU_32B; - pcc->excp_model = POWERPC_EXCP_604; + pcc->excp_model = POWERPC_EXCP_6xx; pcc->bus_model = PPC_FLAGS_INPUT_6xx; pcc->bfd_mach = bfd_mach_ppc_604; pcc->flags = POWERPC_FLAG_SE | POWERPC_FLAG_BE | diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index ae6871a3c0..935f547f25 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -1357,7 +1357,7 @@ static inline void powerpc_excp_legacy(PowerPCCPU *cpu, int excp) srr0 = SPR_BOOKE_CSRR0; srr1 = SPR_BOOKE_CSRR1; break; - case POWERPC_EXCP_G2: + case POWERPC_EXCP_6xx: break; default: goto excp_invalid; @@ -1645,8 +1645,7 @@ static inline void powerpc_excp_legacy(PowerPCCPU *cpu, int excp) case POWERPC_EXCP_DLTLB: /* Data load TLB miss */ case POWERPC_EXCP_DSTLB: /* Data store TLB miss */ switch (excp_model) { - case POWERPC_EXCP_603: - case POWERPC_EXCP_G2: + case POWERPC_EXCP_6xx: /* Swap temporary saved registers with GPRs */ if (!(new_msr & ((target_ulong)1 << MSR_TGPR))) { new_msr |= (target_ulong)1 << MSR_TGPR; From 58d178fb8bb3177777dbc6f1c7946ec4b84b8da6 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Wed, 9 Feb 2022 09:08:55 +0100 Subject: [PATCH 344/460] target/ppc: Introduce powerpc_excp_6xx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce a new powerpc_excp function specific for PowerPC 6xx CPUs (603, 604, G2, MPC5xx, MCP8xx). This commit copies powerpc_excp_legacy verbatim so the next one has a clean diff. Signed-off-by: Fabiano Rosas Message-Id: <20220203200957.1434641-3-farosas@linux.ibm.com> Signed-off-by: Cédric Le Goater --- target/ppc/excp_helper.c | 469 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 469 insertions(+) diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index 935f547f25..daa11e7368 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -549,6 +549,472 @@ static void powerpc_excp_40x(PowerPCCPU *cpu, int excp) powerpc_set_excp_state(cpu, vector, new_msr); } +static void powerpc_excp_6xx(PowerPCCPU *cpu, int excp) +{ + CPUState *cs = CPU(cpu); + CPUPPCState *env = &cpu->env; + int excp_model = env->excp_model; + target_ulong msr, new_msr, vector; + int srr0, srr1, lev = -1; + + if (excp <= POWERPC_EXCP_NONE || excp >= POWERPC_EXCP_NB) { + cpu_abort(cs, "Invalid PowerPC exception %d. Aborting\n", excp); + } + + qemu_log_mask(CPU_LOG_INT, "Raise exception at " TARGET_FMT_lx + " => %s (%d) error=%02x\n", env->nip, powerpc_excp_name(excp), + excp, env->error_code); + + /* new srr1 value excluding must-be-zero bits */ + if (excp_model == POWERPC_EXCP_BOOKE) { + msr = env->msr; + } else { + msr = env->msr & ~0x783f0000ULL; + } + + /* + * new interrupt handler msr preserves existing HV and ME unless + * explicitly overriden + */ + new_msr = env->msr & (((target_ulong)1 << MSR_ME) | MSR_HVB); + + /* target registers */ + srr0 = SPR_SRR0; + srr1 = SPR_SRR1; + + /* + * check for special resume at 0x100 from doze/nap/sleep/winkle on + * P7/P8/P9 + */ + if (env->resume_as_sreset) { + excp = powerpc_reset_wakeup(cs, env, excp, &msr); + } + + /* + * Hypervisor emulation assistance interrupt only exists on server + * arch 2.05 server or later. We also don't want to generate it if + * we don't have HVB in msr_mask (PAPR mode). + */ + if (excp == POWERPC_EXCP_HV_EMU +#if defined(TARGET_PPC64) + && !(mmu_is_64bit(env->mmu_model) && (env->msr_mask & MSR_HVB)) +#endif /* defined(TARGET_PPC64) */ + + ) { + excp = POWERPC_EXCP_PROGRAM; + } + +#ifdef TARGET_PPC64 + /* + * SPEU and VPU share the same IVOR but they exist in different + * processors. SPEU is e500v1/2 only and VPU is e6500 only. + */ + if (excp_model == POWERPC_EXCP_BOOKE && excp == POWERPC_EXCP_VPU) { + excp = POWERPC_EXCP_SPEU; + } +#endif + + vector = env->excp_vectors[excp]; + if (vector == (target_ulong)-1ULL) { + cpu_abort(cs, "Raised an exception without defined vector %d\n", + excp); + } + + vector |= env->excp_prefix; + + switch (excp) { + case POWERPC_EXCP_CRITICAL: /* Critical input */ + switch (excp_model) { + case POWERPC_EXCP_40x: + srr0 = SPR_40x_SRR2; + srr1 = SPR_40x_SRR3; + break; + case POWERPC_EXCP_BOOKE: + srr0 = SPR_BOOKE_CSRR0; + srr1 = SPR_BOOKE_CSRR1; + break; + case POWERPC_EXCP_6xx: + break; + default: + goto excp_invalid; + } + break; + case POWERPC_EXCP_MCHECK: /* Machine check exception */ + if (msr_me == 0) { + /* + * 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"); + } + cs->halted = 1; + cpu_interrupt_exittb(cs); + } + if (env->msr_mask & MSR_HVB) { + /* + * ISA specifies HV, but can be delivered to guest with HV + * clear (e.g., see FWNMI in PAPR). + */ + new_msr |= (target_ulong)MSR_HVB; + } + + /* machine check exceptions don't have ME set */ + new_msr &= ~((target_ulong)1 << MSR_ME); + + /* XXX: should also have something loaded in DAR / DSISR */ + switch (excp_model) { + case POWERPC_EXCP_40x: + srr0 = SPR_40x_SRR2; + srr1 = SPR_40x_SRR3; + break; + case POWERPC_EXCP_BOOKE: + /* FIXME: choose one or the other based on CPU type */ + srr0 = SPR_BOOKE_MCSRR0; + srr1 = SPR_BOOKE_MCSRR1; + + env->spr[SPR_BOOKE_CSRR0] = env->nip; + env->spr[SPR_BOOKE_CSRR1] = msr; + break; + default: + break; + } + break; + case POWERPC_EXCP_DSI: /* Data storage exception */ + trace_ppc_excp_dsi(env->spr[SPR_DSISR], env->spr[SPR_DAR]); + break; + case POWERPC_EXCP_ISI: /* Instruction storage exception */ + trace_ppc_excp_isi(msr, env->nip); + msr |= env->error_code; + break; + case POWERPC_EXCP_EXTERNAL: /* External input */ + { + bool lpes0; + + cs = CPU(cpu); + + /* + * Exception targeting modifiers + * + * LPES0 is supported on POWER7/8/9 + * LPES1 is not supported (old iSeries mode) + * + * On anything else, we behave as if LPES0 is 1 + * (externals don't alter MSR:HV) + */ +#if defined(TARGET_PPC64) + if (excp_model == POWERPC_EXCP_POWER7 || + excp_model == POWERPC_EXCP_POWER8 || + excp_model == POWERPC_EXCP_POWER9 || + excp_model == POWERPC_EXCP_POWER10) { + lpes0 = !!(env->spr[SPR_LPCR] & LPCR_LPES0); + } else +#endif /* defined(TARGET_PPC64) */ + { + lpes0 = true; + } + + if (!lpes0) { + new_msr |= (target_ulong)MSR_HVB; + new_msr |= env->msr & ((target_ulong)1 << MSR_RI); + srr0 = SPR_HSRR0; + srr1 = SPR_HSRR1; + } + if (env->mpic_proxy) { + /* IACK the IRQ on delivery */ + env->spr[SPR_BOOKE_EPR] = ldl_phys(cs->as, env->mpic_iack); + } + break; + } + case POWERPC_EXCP_ALIGN: /* Alignment exception */ + /* Get rS/rD and rA from faulting opcode */ + /* + * Note: the opcode fields will not be set properly for a + * direct store load/store, but nobody cares as nobody + * actually uses direct store segments. + */ + env->spr[SPR_DSISR] |= (env->error_code & 0x03FF0000) >> 16; + break; + case POWERPC_EXCP_PROGRAM: /* Program exception */ + switch (env->error_code & ~0xF) { + case POWERPC_EXCP_FP: + if ((msr_fe0 == 0 && msr_fe1 == 0) || msr_fp == 0) { + trace_ppc_excp_fp_ignore(); + cs->exception_index = POWERPC_EXCP_NONE; + env->error_code = 0; + return; + } + + /* + * FP exceptions always have NIP pointing to the faulting + * instruction, so always use store_next and claim we are + * precise in the MSR. + */ + msr |= 0x00100000; + env->spr[SPR_BOOKE_ESR] = ESR_FP; + break; + case POWERPC_EXCP_INVAL: + trace_ppc_excp_inval(env->nip); + msr |= 0x00080000; + env->spr[SPR_BOOKE_ESR] = ESR_PIL; + break; + case POWERPC_EXCP_PRIV: + msr |= 0x00040000; + env->spr[SPR_BOOKE_ESR] = ESR_PPR; + break; + case POWERPC_EXCP_TRAP: + msr |= 0x00020000; + env->spr[SPR_BOOKE_ESR] = ESR_PTR; + break; + default: + /* Should never occur */ + cpu_abort(cs, "Invalid program exception %d. Aborting\n", + env->error_code); + break; + } + break; + case POWERPC_EXCP_SYSCALL: /* System call exception */ + lev = env->error_code; + + if ((lev == 1) && cpu->vhyp) { + dump_hcall(env); + } else { + dump_syscall(env); + } + + /* + * We need to correct the NIP which in this case is supposed + * to point to the next instruction + */ + env->nip += 4; + + /* "PAPR mode" built-in hypercall emulation */ + if ((lev == 1) && cpu->vhyp) { + PPCVirtualHypervisorClass *vhc = + PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp); + vhc->hypercall(cpu->vhyp, cpu); + return; + } + if (lev == 1) { + new_msr |= (target_ulong)MSR_HVB; + } + break; + case POWERPC_EXCP_SYSCALL_VECTORED: /* scv exception */ + lev = env->error_code; + dump_syscall(env); + env->nip += 4; + new_msr |= env->msr & ((target_ulong)1 << MSR_EE); + new_msr |= env->msr & ((target_ulong)1 << MSR_RI); + + vector += lev * 0x20; + + env->lr = env->nip; + env->ctr = msr; + break; + case POWERPC_EXCP_FPU: /* Floating-point unavailable exception */ + case POWERPC_EXCP_APU: /* Auxiliary processor unavailable */ + case POWERPC_EXCP_DECR: /* Decrementer exception */ + break; + case POWERPC_EXCP_FIT: /* Fixed-interval timer interrupt */ + /* FIT on 4xx */ + trace_ppc_excp_print("FIT"); + break; + case POWERPC_EXCP_WDT: /* Watchdog timer interrupt */ + trace_ppc_excp_print("WDT"); + switch (excp_model) { + case POWERPC_EXCP_BOOKE: + srr0 = SPR_BOOKE_CSRR0; + srr1 = SPR_BOOKE_CSRR1; + break; + default: + break; + } + break; + case POWERPC_EXCP_DTLB: /* Data TLB error */ + case POWERPC_EXCP_ITLB: /* Instruction TLB error */ + break; + case POWERPC_EXCP_DEBUG: /* Debug interrupt */ + if (env->flags & POWERPC_FLAG_DE) { + /* FIXME: choose one or the other based on CPU type */ + srr0 = SPR_BOOKE_DSRR0; + srr1 = SPR_BOOKE_DSRR1; + + env->spr[SPR_BOOKE_CSRR0] = env->nip; + env->spr[SPR_BOOKE_CSRR1] = msr; + + /* DBSR already modified by caller */ + } else { + cpu_abort(cs, "Debug exception triggered on unsupported model\n"); + } + break; + case POWERPC_EXCP_SPEU: /* SPE/embedded floating-point unavailable/VPU */ + env->spr[SPR_BOOKE_ESR] = ESR_SPV; + break; + case POWERPC_EXCP_DOORI: /* Embedded doorbell interrupt */ + break; + case POWERPC_EXCP_DOORCI: /* Embedded doorbell critical interrupt */ + srr0 = SPR_BOOKE_CSRR0; + srr1 = SPR_BOOKE_CSRR1; + break; + case POWERPC_EXCP_RESET: /* System reset exception */ + /* A power-saving exception sets ME, otherwise it is unchanged */ + if (msr_pow) { + /* indicate that we resumed from power save mode */ + msr |= 0x10000; + new_msr |= ((target_ulong)1 << MSR_ME); + } + if (env->msr_mask & MSR_HVB) { + /* + * ISA specifies HV, but can be delivered to guest with HV + * clear (e.g., see FWNMI in PAPR, NMI injection in QEMU). + */ + new_msr |= (target_ulong)MSR_HVB; + } else { + if (msr_pow) { + cpu_abort(cs, "Trying to deliver power-saving system reset " + "exception %d with no HV support\n", excp); + } + } + break; + case POWERPC_EXCP_DSEG: /* Data segment exception */ + case POWERPC_EXCP_ISEG: /* Instruction segment exception */ + case POWERPC_EXCP_TRACE: /* Trace exception */ + break; + case POWERPC_EXCP_HISI: /* Hypervisor instruction storage exception */ + msr |= env->error_code; + /* fall through */ + case POWERPC_EXCP_HDECR: /* Hypervisor decrementer exception */ + case POWERPC_EXCP_HDSI: /* Hypervisor data storage exception */ + case POWERPC_EXCP_HDSEG: /* Hypervisor data segment exception */ + case POWERPC_EXCP_HISEG: /* Hypervisor instruction segment exception */ + case POWERPC_EXCP_SDOOR_HV: /* Hypervisor Doorbell interrupt */ + case POWERPC_EXCP_HV_EMU: + case POWERPC_EXCP_HVIRT: /* Hypervisor virtualization */ + srr0 = SPR_HSRR0; + srr1 = SPR_HSRR1; + new_msr |= (target_ulong)MSR_HVB; + new_msr |= env->msr & ((target_ulong)1 << MSR_RI); + break; + case POWERPC_EXCP_VPU: /* Vector unavailable exception */ + case POWERPC_EXCP_VSXU: /* VSX unavailable exception */ + case POWERPC_EXCP_FU: /* Facility unavailable exception */ +#ifdef TARGET_PPC64 + env->spr[SPR_FSCR] |= ((target_ulong)env->error_code << 56); +#endif + break; + case POWERPC_EXCP_HV_FU: /* Hypervisor Facility Unavailable Exception */ +#ifdef TARGET_PPC64 + env->spr[SPR_HFSCR] |= ((target_ulong)env->error_code << FSCR_IC_POS); + srr0 = SPR_HSRR0; + srr1 = SPR_HSRR1; + new_msr |= (target_ulong)MSR_HVB; + new_msr |= env->msr & ((target_ulong)1 << MSR_RI); +#endif + break; + case POWERPC_EXCP_PIT: /* Programmable interval timer interrupt */ + trace_ppc_excp_print("PIT"); + break; + case POWERPC_EXCP_IFTLB: /* Instruction fetch TLB error */ + case POWERPC_EXCP_DLTLB: /* Data load TLB miss */ + case POWERPC_EXCP_DSTLB: /* Data store TLB miss */ + switch (excp_model) { + case POWERPC_EXCP_6xx: + /* Swap temporary saved registers with GPRs */ + if (!(new_msr & ((target_ulong)1 << MSR_TGPR))) { + new_msr |= (target_ulong)1 << MSR_TGPR; + hreg_swap_gpr_tgpr(env); + } + /* fall through */ + case POWERPC_EXCP_7x5: + ppc_excp_debug_sw_tlb(env, excp); + + msr |= env->crf[0] << 28; + msr |= env->error_code; /* key, D/I, S/L bits */ + /* Set way using a LRU mechanism */ + msr |= ((env->last_way + 1) & (env->nb_ways - 1)) << 17; + break; + default: + cpu_abort(cs, "Invalid TLB miss exception\n"); + break; + } + break; + case POWERPC_EXCP_EFPDI: /* Embedded floating-point data interrupt */ + case POWERPC_EXCP_EFPRI: /* Embedded floating-point round interrupt */ + case POWERPC_EXCP_EPERFM: /* Embedded performance monitor interrupt */ + case POWERPC_EXCP_FPA: /* Floating-point assist exception */ + case POWERPC_EXCP_DABR: /* Data address breakpoint */ + case POWERPC_EXCP_IABR: /* Instruction address breakpoint */ + case POWERPC_EXCP_SMI: /* System management interrupt */ + case POWERPC_EXCP_THERM: /* Thermal interrupt */ + case POWERPC_EXCP_PERFM: /* Embedded performance monitor interrupt */ + case POWERPC_EXCP_VPUA: /* Vector assist exception */ + case POWERPC_EXCP_SOFTP: /* Soft patch exception */ + case POWERPC_EXCP_MAINT: /* Maintenance exception */ + case POWERPC_EXCP_MEXTBR: /* Maskable external breakpoint */ + case POWERPC_EXCP_NMEXTBR: /* Non maskable external breakpoint */ + cpu_abort(cs, "%s exception not implemented\n", + powerpc_excp_name(excp)); + break; + default: + excp_invalid: + cpu_abort(cs, "Invalid PowerPC exception %d. Aborting\n", excp); + break; + } + + /* Sanity check */ + if (!(env->msr_mask & MSR_HVB)) { + if (new_msr & MSR_HVB) { + cpu_abort(cs, "Trying to deliver HV exception (MSR) %d with " + "no HV support\n", excp); + } + if (srr0 == SPR_HSRR0) { + cpu_abort(cs, "Trying to deliver HV exception (HSRR) %d with " + "no HV support\n", excp); + } + } + + /* + * Sort out endianness of interrupt, this differs depending on the + * CPU, the HV mode, etc... + */ + if (ppc_interrupts_little_endian(cpu, !!(new_msr & MSR_HVB))) { + new_msr |= (target_ulong)1 << MSR_LE; + } + +#if defined(TARGET_PPC64) + if (excp_model == POWERPC_EXCP_BOOKE) { + if (env->spr[SPR_BOOKE_EPCR] & EPCR_ICM) { + /* Cat.64-bit: EPCR.ICM is copied to MSR.CM */ + new_msr |= (target_ulong)1 << MSR_CM; + } else { + vector = (uint32_t)vector; + } + } else { + if (!msr_isf && !mmu_is_64bit(env->mmu_model)) { + vector = (uint32_t)vector; + } else { + new_msr |= (target_ulong)1 << MSR_SF; + } + } +#endif + + if (excp != POWERPC_EXCP_SYSCALL_VECTORED) { + /* Save PC */ + env->spr[srr0] = env->nip; + + /* Save MSR */ + env->spr[srr1] = msr; + } + + /* This can update new_msr and vector if AIL applies */ + ppc_excp_apply_ail(cpu, excp_model, excp, msr, &new_msr, &vector); + + powerpc_set_excp_state(cpu, vector, new_msr); +} + static void powerpc_excp_74xx(PowerPCCPU *cpu, int excp) { CPUState *cs = CPU(cpu); @@ -1747,6 +2213,9 @@ static void powerpc_excp(PowerPCCPU *cpu, int excp) case POWERPC_EXCP_40x: powerpc_excp_40x(cpu, excp); break; + case POWERPC_EXCP_6xx: + powerpc_excp_6xx(cpu, excp); + break; case POWERPC_EXCP_74xx: powerpc_excp_74xx(cpu, excp); break; From 082d783bf02eaafa4610fbdd9fe8b1749bcf0e2d Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Wed, 9 Feb 2022 09:08:55 +0100 Subject: [PATCH 345/460] target/ppc: Simplify powerpc_excp_6xx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Differences from the generic powerpc_excp code: - Not BookE, so some MSR bits are cleared at interrupt dispatch; - No MSR_HV; - No power saving states; - No Hypervisor Emulation Assistance; - Not 64 bits; - No System call vectored; - No Alternate Interrupt Location. Exceptions used: POWERPC_EXCP_ALIGN POWERPC_EXCP_CRITICAL POWERPC_EXCP_DABR POWERPC_EXCP_DECR POWERPC_EXCP_DLTLB POWERPC_EXCP_DSI POWERPC_EXCP_DSTLB POWERPC_EXCP_DTLB POWERPC_EXCP_EXTERNAL POWERPC_EXCP_FPA POWERPC_EXCP_FPU POWERPC_EXCP_IABR POWERPC_EXCP_IFTLB POWERPC_EXCP_ISI POWERPC_EXCP_ITLB POWERPC_EXCP_MCHECK POWERPC_EXCP_MEXTBR POWERPC_EXCP_NMEXTBR POWERPC_EXCP_PROGRAM POWERPC_EXCP_RESET POWERPC_EXCP_SMI POWERPC_EXCP_SYSCALL POWERPC_EXCP_TRACE Signed-off-by: Fabiano Rosas Message-Id: <20220203200957.1434641-4-farosas@linux.ibm.com> Signed-off-by: Cédric Le Goater --- target/ppc/excp_helper.c | 163 +++------------------------------------ 1 file changed, 9 insertions(+), 154 deletions(-) diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index daa11e7368..d855a275ca 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -566,54 +566,26 @@ static void powerpc_excp_6xx(PowerPCCPU *cpu, int excp) excp, env->error_code); /* new srr1 value excluding must-be-zero bits */ - if (excp_model == POWERPC_EXCP_BOOKE) { - msr = env->msr; - } else { - msr = env->msr & ~0x783f0000ULL; - } + msr = env->msr & ~0x783f0000ULL; /* - * new interrupt handler msr preserves existing HV and ME unless + * new interrupt handler msr preserves existing ME unless * explicitly overriden */ - new_msr = env->msr & (((target_ulong)1 << MSR_ME) | MSR_HVB); + new_msr = env->msr & ((target_ulong)1 << MSR_ME); /* target registers */ srr0 = SPR_SRR0; srr1 = SPR_SRR1; - /* - * check for special resume at 0x100 from doze/nap/sleep/winkle on - * P7/P8/P9 - */ - if (env->resume_as_sreset) { - excp = powerpc_reset_wakeup(cs, env, excp, &msr); - } - /* * Hypervisor emulation assistance interrupt only exists on server - * arch 2.05 server or later. We also don't want to generate it if - * we don't have HVB in msr_mask (PAPR mode). + * arch 2.05 server or later. */ - if (excp == POWERPC_EXCP_HV_EMU -#if defined(TARGET_PPC64) - && !(mmu_is_64bit(env->mmu_model) && (env->msr_mask & MSR_HVB)) -#endif /* defined(TARGET_PPC64) */ - - ) { + if (excp == POWERPC_EXCP_HV_EMU) { excp = POWERPC_EXCP_PROGRAM; } -#ifdef TARGET_PPC64 - /* - * SPEU and VPU share the same IVOR but they exist in different - * processors. SPEU is e500v1/2 only and VPU is e6500 only. - */ - if (excp_model == POWERPC_EXCP_BOOKE && excp == POWERPC_EXCP_VPU) { - excp = POWERPC_EXCP_SPEU; - } -#endif - vector = env->excp_vectors[excp]; if (vector == (target_ulong)-1ULL) { cpu_abort(cs, "Raised an exception without defined vector %d\n", @@ -802,63 +774,12 @@ static void powerpc_excp_6xx(PowerPCCPU *cpu, int excp) new_msr |= (target_ulong)MSR_HVB; } break; - case POWERPC_EXCP_SYSCALL_VECTORED: /* scv exception */ - lev = env->error_code; - dump_syscall(env); - env->nip += 4; - new_msr |= env->msr & ((target_ulong)1 << MSR_EE); - new_msr |= env->msr & ((target_ulong)1 << MSR_RI); - - vector += lev * 0x20; - - env->lr = env->nip; - env->ctr = msr; - break; case POWERPC_EXCP_FPU: /* Floating-point unavailable exception */ - case POWERPC_EXCP_APU: /* Auxiliary processor unavailable */ case POWERPC_EXCP_DECR: /* Decrementer exception */ break; - case POWERPC_EXCP_FIT: /* Fixed-interval timer interrupt */ - /* FIT on 4xx */ - trace_ppc_excp_print("FIT"); - break; - case POWERPC_EXCP_WDT: /* Watchdog timer interrupt */ - trace_ppc_excp_print("WDT"); - switch (excp_model) { - case POWERPC_EXCP_BOOKE: - srr0 = SPR_BOOKE_CSRR0; - srr1 = SPR_BOOKE_CSRR1; - break; - default: - break; - } - break; case POWERPC_EXCP_DTLB: /* Data TLB error */ case POWERPC_EXCP_ITLB: /* Instruction TLB error */ break; - case POWERPC_EXCP_DEBUG: /* Debug interrupt */ - if (env->flags & POWERPC_FLAG_DE) { - /* FIXME: choose one or the other based on CPU type */ - srr0 = SPR_BOOKE_DSRR0; - srr1 = SPR_BOOKE_DSRR1; - - env->spr[SPR_BOOKE_CSRR0] = env->nip; - env->spr[SPR_BOOKE_CSRR1] = msr; - - /* DBSR already modified by caller */ - } else { - cpu_abort(cs, "Debug exception triggered on unsupported model\n"); - } - break; - case POWERPC_EXCP_SPEU: /* SPE/embedded floating-point unavailable/VPU */ - env->spr[SPR_BOOKE_ESR] = ESR_SPV; - break; - case POWERPC_EXCP_DOORI: /* Embedded doorbell interrupt */ - break; - case POWERPC_EXCP_DOORCI: /* Embedded doorbell critical interrupt */ - srr0 = SPR_BOOKE_CSRR0; - srr1 = SPR_BOOKE_CSRR1; - break; case POWERPC_EXCP_RESET: /* System reset exception */ /* A power-saving exception sets ME, otherwise it is unchanged */ if (msr_pow) { @@ -879,44 +800,8 @@ static void powerpc_excp_6xx(PowerPCCPU *cpu, int excp) } } break; - case POWERPC_EXCP_DSEG: /* Data segment exception */ - case POWERPC_EXCP_ISEG: /* Instruction segment exception */ case POWERPC_EXCP_TRACE: /* Trace exception */ break; - case POWERPC_EXCP_HISI: /* Hypervisor instruction storage exception */ - msr |= env->error_code; - /* fall through */ - case POWERPC_EXCP_HDECR: /* Hypervisor decrementer exception */ - case POWERPC_EXCP_HDSI: /* Hypervisor data storage exception */ - case POWERPC_EXCP_HDSEG: /* Hypervisor data segment exception */ - case POWERPC_EXCP_HISEG: /* Hypervisor instruction segment exception */ - case POWERPC_EXCP_SDOOR_HV: /* Hypervisor Doorbell interrupt */ - case POWERPC_EXCP_HV_EMU: - case POWERPC_EXCP_HVIRT: /* Hypervisor virtualization */ - srr0 = SPR_HSRR0; - srr1 = SPR_HSRR1; - new_msr |= (target_ulong)MSR_HVB; - new_msr |= env->msr & ((target_ulong)1 << MSR_RI); - break; - case POWERPC_EXCP_VPU: /* Vector unavailable exception */ - case POWERPC_EXCP_VSXU: /* VSX unavailable exception */ - case POWERPC_EXCP_FU: /* Facility unavailable exception */ -#ifdef TARGET_PPC64 - env->spr[SPR_FSCR] |= ((target_ulong)env->error_code << 56); -#endif - break; - case POWERPC_EXCP_HV_FU: /* Hypervisor Facility Unavailable Exception */ -#ifdef TARGET_PPC64 - env->spr[SPR_HFSCR] |= ((target_ulong)env->error_code << FSCR_IC_POS); - srr0 = SPR_HSRR0; - srr1 = SPR_HSRR1; - new_msr |= (target_ulong)MSR_HVB; - new_msr |= env->msr & ((target_ulong)1 << MSR_RI); -#endif - break; - case POWERPC_EXCP_PIT: /* Programmable interval timer interrupt */ - trace_ppc_excp_print("PIT"); - break; case POWERPC_EXCP_IFTLB: /* Instruction fetch TLB error */ case POWERPC_EXCP_DLTLB: /* Data load TLB miss */ case POWERPC_EXCP_DSTLB: /* Data store TLB miss */ @@ -941,18 +826,10 @@ static void powerpc_excp_6xx(PowerPCCPU *cpu, int excp) break; } break; - case POWERPC_EXCP_EFPDI: /* Embedded floating-point data interrupt */ - case POWERPC_EXCP_EFPRI: /* Embedded floating-point round interrupt */ - case POWERPC_EXCP_EPERFM: /* Embedded performance monitor interrupt */ case POWERPC_EXCP_FPA: /* Floating-point assist exception */ case POWERPC_EXCP_DABR: /* Data address breakpoint */ case POWERPC_EXCP_IABR: /* Instruction address breakpoint */ case POWERPC_EXCP_SMI: /* System management interrupt */ - case POWERPC_EXCP_THERM: /* Thermal interrupt */ - case POWERPC_EXCP_PERFM: /* Embedded performance monitor interrupt */ - case POWERPC_EXCP_VPUA: /* Vector assist exception */ - case POWERPC_EXCP_SOFTP: /* Soft patch exception */ - case POWERPC_EXCP_MAINT: /* Maintenance exception */ case POWERPC_EXCP_MEXTBR: /* Maskable external breakpoint */ case POWERPC_EXCP_NMEXTBR: /* Non maskable external breakpoint */ cpu_abort(cs, "%s exception not implemented\n", @@ -984,33 +861,11 @@ static void powerpc_excp_6xx(PowerPCCPU *cpu, int excp) new_msr |= (target_ulong)1 << MSR_LE; } -#if defined(TARGET_PPC64) - if (excp_model == POWERPC_EXCP_BOOKE) { - if (env->spr[SPR_BOOKE_EPCR] & EPCR_ICM) { - /* Cat.64-bit: EPCR.ICM is copied to MSR.CM */ - new_msr |= (target_ulong)1 << MSR_CM; - } else { - vector = (uint32_t)vector; - } - } else { - if (!msr_isf && !mmu_is_64bit(env->mmu_model)) { - vector = (uint32_t)vector; - } else { - new_msr |= (target_ulong)1 << MSR_SF; - } - } -#endif + /* Save PC */ + env->spr[srr0] = env->nip; - if (excp != POWERPC_EXCP_SYSCALL_VECTORED) { - /* Save PC */ - env->spr[srr0] = env->nip; - - /* Save MSR */ - env->spr[srr1] = msr; - } - - /* This can update new_msr and vector if AIL applies */ - ppc_excp_apply_ail(cpu, excp_model, excp, msr, &new_msr, &vector); + /* Save MSR */ + env->spr[srr1] = msr; powerpc_set_excp_state(cpu, vector, new_msr); } From b7c32cdd9a7cd226fa41d49c4503b8f15c74cf7f Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Wed, 9 Feb 2022 09:08:55 +0100 Subject: [PATCH 346/460] target/ppc: 6xx: Critical exception cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This only applies to the G2s, the other 6xx CPUs will not have this vector registered. Signed-off-by: Fabiano Rosas Message-Id: <20220203200957.1434641-5-farosas@linux.ibm.com> Signed-off-by: Cédric Le Goater --- target/ppc/excp_helper.c | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index d855a275ca..e27e1c3c70 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -596,20 +596,6 @@ static void powerpc_excp_6xx(PowerPCCPU *cpu, int excp) switch (excp) { case POWERPC_EXCP_CRITICAL: /* Critical input */ - switch (excp_model) { - case POWERPC_EXCP_40x: - srr0 = SPR_40x_SRR2; - srr1 = SPR_40x_SRR3; - break; - case POWERPC_EXCP_BOOKE: - srr0 = SPR_BOOKE_CSRR0; - srr1 = SPR_BOOKE_CSRR1; - break; - case POWERPC_EXCP_6xx: - break; - default: - goto excp_invalid; - } break; case POWERPC_EXCP_MCHECK: /* Machine check exception */ if (msr_me == 0) { @@ -836,7 +822,6 @@ static void powerpc_excp_6xx(PowerPCCPU *cpu, int excp) powerpc_excp_name(excp)); break; default: - excp_invalid: cpu_abort(cs, "Invalid PowerPC exception %d. Aborting\n", excp); break; } From 9b12ff43d428805d79315521ff2d242e67bb47ce Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Wed, 9 Feb 2022 09:08:55 +0100 Subject: [PATCH 347/460] target/ppc: 6xx: Machine Check exception cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There's no MSR_HV in the 6xx CPUs. Also remove the 40x and BookE code. Signed-off-by: Fabiano Rosas Message-Id: <20220203200957.1434641-6-farosas@linux.ibm.com> Signed-off-by: Cédric Le Goater --- target/ppc/excp_helper.c | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index e27e1c3c70..734170d4c2 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -612,34 +612,10 @@ static void powerpc_excp_6xx(PowerPCCPU *cpu, int excp) cs->halted = 1; cpu_interrupt_exittb(cs); } - if (env->msr_mask & MSR_HVB) { - /* - * ISA specifies HV, but can be delivered to guest with HV - * clear (e.g., see FWNMI in PAPR). - */ - new_msr |= (target_ulong)MSR_HVB; - } /* machine check exceptions don't have ME set */ new_msr &= ~((target_ulong)1 << MSR_ME); - /* XXX: should also have something loaded in DAR / DSISR */ - switch (excp_model) { - case POWERPC_EXCP_40x: - srr0 = SPR_40x_SRR2; - srr1 = SPR_40x_SRR3; - break; - case POWERPC_EXCP_BOOKE: - /* FIXME: choose one or the other based on CPU type */ - srr0 = SPR_BOOKE_MCSRR0; - srr1 = SPR_BOOKE_MCSRR1; - - env->spr[SPR_BOOKE_CSRR0] = env->nip; - env->spr[SPR_BOOKE_CSRR1] = msr; - break; - default: - break; - } break; case POWERPC_EXCP_DSI: /* Data storage exception */ trace_ppc_excp_dsi(env->spr[SPR_DSISR], env->spr[SPR_DAR]); From 3189fa391702314b84edbf0edbf25a5c08ccad9e Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Wed, 9 Feb 2022 09:08:55 +0100 Subject: [PATCH 348/460] target/ppc: 6xx: External interrupt cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There's no Hypervisor mode in the 6xx, so remove all LPES0 logic. Also remove BookE IRQ code. Signed-off-by: Fabiano Rosas Message-Id: <20220203200957.1434641-7-farosas@linux.ibm.com> Signed-off-by: Cédric Le Goater --- target/ppc/excp_helper.c | 37 ------------------------------------- 1 file changed, 37 deletions(-) diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index 734170d4c2..a008115e5f 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -625,44 +625,7 @@ static void powerpc_excp_6xx(PowerPCCPU *cpu, int excp) msr |= env->error_code; break; case POWERPC_EXCP_EXTERNAL: /* External input */ - { - bool lpes0; - - cs = CPU(cpu); - - /* - * Exception targeting modifiers - * - * LPES0 is supported on POWER7/8/9 - * LPES1 is not supported (old iSeries mode) - * - * On anything else, we behave as if LPES0 is 1 - * (externals don't alter MSR:HV) - */ -#if defined(TARGET_PPC64) - if (excp_model == POWERPC_EXCP_POWER7 || - excp_model == POWERPC_EXCP_POWER8 || - excp_model == POWERPC_EXCP_POWER9 || - excp_model == POWERPC_EXCP_POWER10) { - lpes0 = !!(env->spr[SPR_LPCR] & LPCR_LPES0); - } else -#endif /* defined(TARGET_PPC64) */ - { - lpes0 = true; - } - - if (!lpes0) { - new_msr |= (target_ulong)MSR_HVB; - new_msr |= env->msr & ((target_ulong)1 << MSR_RI); - srr0 = SPR_HSRR0; - srr1 = SPR_HSRR1; - } - if (env->mpic_proxy) { - /* IACK the IRQ on delivery */ - env->spr[SPR_BOOKE_EPR] = ldl_phys(cs->as, env->mpic_iack); - } break; - } case POWERPC_EXCP_ALIGN: /* Alignment exception */ /* Get rS/rD and rA from faulting opcode */ /* From 25fe5f7534ee9dce6c2f038b1bd118e5d2eb5354 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Wed, 9 Feb 2022 09:08:55 +0100 Subject: [PATCH 349/460] target/ppc: 6xx: Program exception cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There's no ESR in the 6xx CPUs. Signed-off-by: Fabiano Rosas Message-Id: <20220203200957.1434641-8-farosas@linux.ibm.com> Signed-off-by: Cédric Le Goater --- target/ppc/excp_helper.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index a008115e5f..a195288dda 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -651,20 +651,16 @@ static void powerpc_excp_6xx(PowerPCCPU *cpu, int excp) * precise in the MSR. */ msr |= 0x00100000; - env->spr[SPR_BOOKE_ESR] = ESR_FP; break; case POWERPC_EXCP_INVAL: trace_ppc_excp_inval(env->nip); msr |= 0x00080000; - env->spr[SPR_BOOKE_ESR] = ESR_PIL; break; case POWERPC_EXCP_PRIV: msr |= 0x00040000; - env->spr[SPR_BOOKE_ESR] = ESR_PPR; break; case POWERPC_EXCP_TRAP: msr |= 0x00020000; - env->spr[SPR_BOOKE_ESR] = ESR_PTR; break; default: /* Should never occur */ From c0e1928de5ff1152c4dafe36caf334e6fa10aad4 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Wed, 9 Feb 2022 09:08:55 +0100 Subject: [PATCH 350/460] target/ppc: 6xx: System Call exception cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is no Hypervisor mode in the 6xx CPUs. Signed-off-by: Fabiano Rosas Message-Id: <20220203200957.1434641-9-farosas@linux.ibm.com> Signed-off-by: Cédric Le Goater --- target/ppc/excp_helper.c | 21 ++------------------- 1 file changed, 2 insertions(+), 19 deletions(-) diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index a195288dda..28d9a9a887 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -555,7 +555,7 @@ static void powerpc_excp_6xx(PowerPCCPU *cpu, int excp) CPUPPCState *env = &cpu->env; int excp_model = env->excp_model; target_ulong msr, new_msr, vector; - int srr0, srr1, lev = -1; + int srr0, srr1; if (excp <= POWERPC_EXCP_NONE || excp >= POWERPC_EXCP_NB) { cpu_abort(cs, "Invalid PowerPC exception %d. Aborting\n", excp); @@ -670,30 +670,13 @@ static void powerpc_excp_6xx(PowerPCCPU *cpu, int excp) } break; case POWERPC_EXCP_SYSCALL: /* System call exception */ - lev = env->error_code; - - if ((lev == 1) && cpu->vhyp) { - dump_hcall(env); - } else { - dump_syscall(env); - } + dump_syscall(env); /* * We need to correct the NIP which in this case is supposed * to point to the next instruction */ env->nip += 4; - - /* "PAPR mode" built-in hypercall emulation */ - if ((lev == 1) && cpu->vhyp) { - PPCVirtualHypervisorClass *vhc = - PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp); - vhc->hypercall(cpu->vhyp, cpu); - return; - } - if (lev == 1) { - new_msr |= (target_ulong)MSR_HVB; - } break; case POWERPC_EXCP_FPU: /* Floating-point unavailable exception */ case POWERPC_EXCP_DECR: /* Decrementer exception */ From 000ac49ad22be56e8ab21550e90e997b9ee39575 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Wed, 9 Feb 2022 09:08:55 +0100 Subject: [PATCH 351/460] target/ppc: 6xx: System Reset interrupt cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is no HV support in the 6xx. Signed-off-by: Fabiano Rosas Message-Id: <20220203200957.1434641-10-farosas@linux.ibm.com> Signed-off-by: Cédric Le Goater --- target/ppc/excp_helper.c | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index 28d9a9a887..538905c4dd 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -685,23 +685,9 @@ static void powerpc_excp_6xx(PowerPCCPU *cpu, int excp) case POWERPC_EXCP_ITLB: /* Instruction TLB error */ break; case POWERPC_EXCP_RESET: /* System reset exception */ - /* A power-saving exception sets ME, otherwise it is unchanged */ if (msr_pow) { - /* indicate that we resumed from power save mode */ - msr |= 0x10000; - new_msr |= ((target_ulong)1 << MSR_ME); - } - if (env->msr_mask & MSR_HVB) { - /* - * ISA specifies HV, but can be delivered to guest with HV - * clear (e.g., see FWNMI in PAPR, NMI injection in QEMU). - */ - new_msr |= (target_ulong)MSR_HVB; - } else { - if (msr_pow) { - cpu_abort(cs, "Trying to deliver power-saving system reset " - "exception %d with no HV support\n", excp); - } + cpu_abort(cs, "Trying to deliver power-saving system reset " + "exception %d with no HV support\n", excp); } break; case POWERPC_EXCP_TRACE: /* Trace exception */ From 8f8c7932d4be3fe4dc0fb9540c48132de7382cab Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Wed, 9 Feb 2022 09:08:55 +0100 Subject: [PATCH 352/460] target/ppc: 6xx: Software TLB exceptions cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This code applies only to the 6xx CPUs, so we can remove the switch statement. Signed-off-by: Fabiano Rosas Message-Id: <20220203200957.1434641-11-farosas@linux.ibm.com> Signed-off-by: Cédric Le Goater --- target/ppc/excp_helper.c | 31 +++++++++++-------------------- 1 file changed, 11 insertions(+), 20 deletions(-) diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index 538905c4dd..80168355bd 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -553,7 +553,6 @@ static void powerpc_excp_6xx(PowerPCCPU *cpu, int excp) { CPUState *cs = CPU(cpu); CPUPPCState *env = &cpu->env; - int excp_model = env->excp_model; target_ulong msr, new_msr, vector; int srr0, srr1; @@ -695,26 +694,18 @@ static void powerpc_excp_6xx(PowerPCCPU *cpu, int excp) case POWERPC_EXCP_IFTLB: /* Instruction fetch TLB error */ case POWERPC_EXCP_DLTLB: /* Data load TLB miss */ case POWERPC_EXCP_DSTLB: /* Data store TLB miss */ - switch (excp_model) { - case POWERPC_EXCP_6xx: - /* Swap temporary saved registers with GPRs */ - if (!(new_msr & ((target_ulong)1 << MSR_TGPR))) { - new_msr |= (target_ulong)1 << MSR_TGPR; - hreg_swap_gpr_tgpr(env); - } - /* fall through */ - case POWERPC_EXCP_7x5: - ppc_excp_debug_sw_tlb(env, excp); - - msr |= env->crf[0] << 28; - msr |= env->error_code; /* key, D/I, S/L bits */ - /* Set way using a LRU mechanism */ - msr |= ((env->last_way + 1) & (env->nb_ways - 1)) << 17; - break; - default: - cpu_abort(cs, "Invalid TLB miss exception\n"); - break; + /* Swap temporary saved registers with GPRs */ + if (!(new_msr & ((target_ulong)1 << MSR_TGPR))) { + new_msr |= (target_ulong)1 << MSR_TGPR; + hreg_swap_gpr_tgpr(env); } + + ppc_excp_debug_sw_tlb(env, excp); + + msr |= env->crf[0] << 28; + msr |= env->error_code; /* key, D/I, S/L bits */ + /* Set way using a LRU mechanism */ + msr |= ((env->last_way + 1) & (env->nb_ways - 1)) << 17; break; case POWERPC_EXCP_FPA: /* Floating-point assist exception */ case POWERPC_EXCP_DABR: /* Data address breakpoint */ From c50eaed135216597cd75f71cec79ae28a7996c06 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Wed, 9 Feb 2022 09:08:56 +0100 Subject: [PATCH 353/460] target/ppc: 6xx: Set SRRs directly in exception code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 6xx CPUs don't have alternate/hypervisor Save and Restore Registers, so we can set SRR0 and SRR1 directly. Signed-off-by: Fabiano Rosas Message-Id: <20220203200957.1434641-12-farosas@linux.ibm.com> Signed-off-by: Cédric Le Goater --- target/ppc/excp_helper.c | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index 80168355bd..7bdda6f165 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -554,7 +554,6 @@ static void powerpc_excp_6xx(PowerPCCPU *cpu, int excp) CPUState *cs = CPU(cpu); CPUPPCState *env = &cpu->env; target_ulong msr, new_msr, vector; - int srr0, srr1; if (excp <= POWERPC_EXCP_NONE || excp >= POWERPC_EXCP_NB) { cpu_abort(cs, "Invalid PowerPC exception %d. Aborting\n", excp); @@ -573,10 +572,6 @@ static void powerpc_excp_6xx(PowerPCCPU *cpu, int excp) */ new_msr = env->msr & ((target_ulong)1 << MSR_ME); - /* target registers */ - srr0 = SPR_SRR0; - srr1 = SPR_SRR1; - /* * Hypervisor emulation assistance interrupt only exists on server * arch 2.05 server or later. @@ -727,10 +722,6 @@ static void powerpc_excp_6xx(PowerPCCPU *cpu, int excp) cpu_abort(cs, "Trying to deliver HV exception (MSR) %d with " "no HV support\n", excp); } - if (srr0 == SPR_HSRR0) { - cpu_abort(cs, "Trying to deliver HV exception (HSRR) %d with " - "no HV support\n", excp); - } } /* @@ -742,10 +733,10 @@ static void powerpc_excp_6xx(PowerPCCPU *cpu, int excp) } /* Save PC */ - env->spr[srr0] = env->nip; + env->spr[SPR_SRR0] = env->nip; /* Save MSR */ - env->spr[srr1] = msr; + env->spr[SPR_SRR1] = msr; powerpc_set_excp_state(cpu, vector, new_msr); } From fd7dc4bb7869e367f5b9c6934abbb13aa04a21f6 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Wed, 9 Feb 2022 09:08:56 +0100 Subject: [PATCH 354/460] target/ppc: Merge 7x5 and 7x0 exception model IDs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since we've split the exception code by exception model, the exception model IDs are becoming less useful. These two can be merged. Signed-off-by: Fabiano Rosas Message-Id: <20220204173430.1457358-2-farosas@linux.ibm.com> Signed-off-by: Cédric Le Goater --- target/ppc/cpu-qom.h | 6 ++---- target/ppc/cpu_init.c | 16 ++++++++-------- target/ppc/excp_helper.c | 2 +- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/target/ppc/cpu-qom.h b/target/ppc/cpu-qom.h index 3880fb3337..98facee9fa 100644 --- a/target/ppc/cpu-qom.h +++ b/target/ppc/cpu-qom.h @@ -90,10 +90,8 @@ enum powerpc_excp_t { POWERPC_EXCP_40x, /* PowerPC 603/604/G2 exception model */ POWERPC_EXCP_6xx, - /* PowerPC 7x0 exception model */ - POWERPC_EXCP_7x0, - /* PowerPC 7x5 exception model */ - POWERPC_EXCP_7x5, + /* PowerPC 7xx exception model */ + POWERPC_EXCP_7xx, /* PowerPC 74xx exception model */ POWERPC_EXCP_74xx, /* BookE exception model */ diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index 95c5831ba6..d97f718354 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -4021,7 +4021,7 @@ POWERPC_FAMILY(740)(ObjectClass *oc, void *data) (1ull << MSR_RI) | (1ull << MSR_LE); pcc->mmu_model = POWERPC_MMU_32B; - pcc->excp_model = POWERPC_EXCP_7x0; + pcc->excp_model = POWERPC_EXCP_7xx; pcc->bus_model = PPC_FLAGS_INPUT_6xx; pcc->bfd_mach = bfd_mach_ppc_750; pcc->flags = POWERPC_FLAG_SE | POWERPC_FLAG_BE | @@ -4098,7 +4098,7 @@ POWERPC_FAMILY(750)(ObjectClass *oc, void *data) (1ull << MSR_RI) | (1ull << MSR_LE); pcc->mmu_model = POWERPC_MMU_32B; - pcc->excp_model = POWERPC_EXCP_7x0; + pcc->excp_model = POWERPC_EXCP_7xx; pcc->bus_model = PPC_FLAGS_INPUT_6xx; pcc->bfd_mach = bfd_mach_ppc_750; pcc->flags = POWERPC_FLAG_SE | POWERPC_FLAG_BE | @@ -4298,7 +4298,7 @@ POWERPC_FAMILY(750cl)(ObjectClass *oc, void *data) (1ull << MSR_RI) | (1ull << MSR_LE); pcc->mmu_model = POWERPC_MMU_32B; - pcc->excp_model = POWERPC_EXCP_7x0; + pcc->excp_model = POWERPC_EXCP_7xx; pcc->bus_model = PPC_FLAGS_INPUT_6xx; pcc->bfd_mach = bfd_mach_ppc_750; pcc->flags = POWERPC_FLAG_SE | POWERPC_FLAG_BE | @@ -4378,7 +4378,7 @@ POWERPC_FAMILY(750cx)(ObjectClass *oc, void *data) (1ull << MSR_RI) | (1ull << MSR_LE); pcc->mmu_model = POWERPC_MMU_32B; - pcc->excp_model = POWERPC_EXCP_7x0; + pcc->excp_model = POWERPC_EXCP_7xx; pcc->bus_model = PPC_FLAGS_INPUT_6xx; pcc->bfd_mach = bfd_mach_ppc_750; pcc->flags = POWERPC_FLAG_SE | POWERPC_FLAG_BE | @@ -4463,7 +4463,7 @@ POWERPC_FAMILY(750fx)(ObjectClass *oc, void *data) (1ull << MSR_RI) | (1ull << MSR_LE); pcc->mmu_model = POWERPC_MMU_32B; - pcc->excp_model = POWERPC_EXCP_7x0; + pcc->excp_model = POWERPC_EXCP_7xx; pcc->bus_model = PPC_FLAGS_INPUT_6xx; pcc->bfd_mach = bfd_mach_ppc_750; pcc->flags = POWERPC_FLAG_SE | POWERPC_FLAG_BE | @@ -4548,7 +4548,7 @@ POWERPC_FAMILY(750gx)(ObjectClass *oc, void *data) (1ull << MSR_RI) | (1ull << MSR_LE); pcc->mmu_model = POWERPC_MMU_32B; - pcc->excp_model = POWERPC_EXCP_7x0; + pcc->excp_model = POWERPC_EXCP_7xx; pcc->bus_model = PPC_FLAGS_INPUT_6xx; pcc->bfd_mach = bfd_mach_ppc_750; pcc->flags = POWERPC_FLAG_SE | POWERPC_FLAG_BE | @@ -4624,7 +4624,7 @@ POWERPC_FAMILY(745)(ObjectClass *oc, void *data) (1ull << MSR_RI) | (1ull << MSR_LE); pcc->mmu_model = POWERPC_MMU_SOFT_6xx; - pcc->excp_model = POWERPC_EXCP_7x5; + pcc->excp_model = POWERPC_EXCP_7xx; pcc->bus_model = PPC_FLAGS_INPUT_6xx; pcc->bfd_mach = bfd_mach_ppc_750; pcc->flags = POWERPC_FLAG_SE | POWERPC_FLAG_BE | @@ -4711,7 +4711,7 @@ POWERPC_FAMILY(755)(ObjectClass *oc, void *data) (1ull << MSR_RI) | (1ull << MSR_LE); pcc->mmu_model = POWERPC_MMU_SOFT_6xx; - pcc->excp_model = POWERPC_EXCP_7x5; + pcc->excp_model = POWERPC_EXCP_7xx; pcc->bus_model = PPC_FLAGS_INPUT_6xx; pcc->bfd_mach = bfd_mach_ppc_750; pcc->flags = POWERPC_FLAG_SE | POWERPC_FLAG_BE | diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index 7bdda6f165..b6a5c36fd2 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -1844,7 +1844,7 @@ static inline void powerpc_excp_legacy(PowerPCCPU *cpu, int excp) hreg_swap_gpr_tgpr(env); } /* fall through */ - case POWERPC_EXCP_7x5: + case POWERPC_EXCP_7xx: ppc_excp_debug_sw_tlb(env, excp); msr |= env->crf[0] << 28; From ccfca2fca5ef30078a3b8ed011556b55a098de8c Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Wed, 9 Feb 2022 09:08:56 +0100 Subject: [PATCH 355/460] target/ppc: Introduce powerpc_excp_7xx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce a new powerpc_excp function specific for PowerPC 7xx CPUs (740, 745, 750, 750cl, 750cx, 750fx, 750gx, 755). This commit copies powerpc_excp_legacy verbatim so the next one has a clean diff. Signed-off-by: Fabiano Rosas Message-Id: <20220204173430.1457358-3-farosas@linux.ibm.com> Signed-off-by: Cédric Le Goater --- target/ppc/excp_helper.c | 469 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 469 insertions(+) diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index b6a5c36fd2..df96f620b2 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -741,6 +741,472 @@ static void powerpc_excp_6xx(PowerPCCPU *cpu, int excp) powerpc_set_excp_state(cpu, vector, new_msr); } +static void powerpc_excp_7xx(PowerPCCPU *cpu, int excp) +{ + CPUState *cs = CPU(cpu); + CPUPPCState *env = &cpu->env; + int excp_model = env->excp_model; + target_ulong msr, new_msr, vector; + int srr0, srr1, lev = -1; + + if (excp <= POWERPC_EXCP_NONE || excp >= POWERPC_EXCP_NB) { + cpu_abort(cs, "Invalid PowerPC exception %d. Aborting\n", excp); + } + + qemu_log_mask(CPU_LOG_INT, "Raise exception at " TARGET_FMT_lx + " => %s (%d) error=%02x\n", env->nip, powerpc_excp_name(excp), + excp, env->error_code); + + /* new srr1 value excluding must-be-zero bits */ + if (excp_model == POWERPC_EXCP_BOOKE) { + msr = env->msr; + } else { + msr = env->msr & ~0x783f0000ULL; + } + + /* + * new interrupt handler msr preserves existing HV and ME unless + * explicitly overriden + */ + new_msr = env->msr & (((target_ulong)1 << MSR_ME) | MSR_HVB); + + /* target registers */ + srr0 = SPR_SRR0; + srr1 = SPR_SRR1; + + /* + * check for special resume at 0x100 from doze/nap/sleep/winkle on + * P7/P8/P9 + */ + if (env->resume_as_sreset) { + excp = powerpc_reset_wakeup(cs, env, excp, &msr); + } + + /* + * Hypervisor emulation assistance interrupt only exists on server + * arch 2.05 server or later. We also don't want to generate it if + * we don't have HVB in msr_mask (PAPR mode). + */ + if (excp == POWERPC_EXCP_HV_EMU +#if defined(TARGET_PPC64) + && !(mmu_is_64bit(env->mmu_model) && (env->msr_mask & MSR_HVB)) +#endif /* defined(TARGET_PPC64) */ + + ) { + excp = POWERPC_EXCP_PROGRAM; + } + +#ifdef TARGET_PPC64 + /* + * SPEU and VPU share the same IVOR but they exist in different + * processors. SPEU is e500v1/2 only and VPU is e6500 only. + */ + if (excp_model == POWERPC_EXCP_BOOKE && excp == POWERPC_EXCP_VPU) { + excp = POWERPC_EXCP_SPEU; + } +#endif + + vector = env->excp_vectors[excp]; + if (vector == (target_ulong)-1ULL) { + cpu_abort(cs, "Raised an exception without defined vector %d\n", + excp); + } + + vector |= env->excp_prefix; + + switch (excp) { + case POWERPC_EXCP_CRITICAL: /* Critical input */ + switch (excp_model) { + case POWERPC_EXCP_40x: + srr0 = SPR_40x_SRR2; + srr1 = SPR_40x_SRR3; + break; + case POWERPC_EXCP_BOOKE: + srr0 = SPR_BOOKE_CSRR0; + srr1 = SPR_BOOKE_CSRR1; + break; + case POWERPC_EXCP_6xx: + break; + default: + goto excp_invalid; + } + break; + case POWERPC_EXCP_MCHECK: /* Machine check exception */ + if (msr_me == 0) { + /* + * 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"); + } + cs->halted = 1; + cpu_interrupt_exittb(cs); + } + if (env->msr_mask & MSR_HVB) { + /* + * ISA specifies HV, but can be delivered to guest with HV + * clear (e.g., see FWNMI in PAPR). + */ + new_msr |= (target_ulong)MSR_HVB; + } + + /* machine check exceptions don't have ME set */ + new_msr &= ~((target_ulong)1 << MSR_ME); + + /* XXX: should also have something loaded in DAR / DSISR */ + switch (excp_model) { + case POWERPC_EXCP_40x: + srr0 = SPR_40x_SRR2; + srr1 = SPR_40x_SRR3; + break; + case POWERPC_EXCP_BOOKE: + /* FIXME: choose one or the other based on CPU type */ + srr0 = SPR_BOOKE_MCSRR0; + srr1 = SPR_BOOKE_MCSRR1; + + env->spr[SPR_BOOKE_CSRR0] = env->nip; + env->spr[SPR_BOOKE_CSRR1] = msr; + break; + default: + break; + } + break; + case POWERPC_EXCP_DSI: /* Data storage exception */ + trace_ppc_excp_dsi(env->spr[SPR_DSISR], env->spr[SPR_DAR]); + break; + case POWERPC_EXCP_ISI: /* Instruction storage exception */ + trace_ppc_excp_isi(msr, env->nip); + msr |= env->error_code; + break; + case POWERPC_EXCP_EXTERNAL: /* External input */ + { + bool lpes0; + + cs = CPU(cpu); + + /* + * Exception targeting modifiers + * + * LPES0 is supported on POWER7/8/9 + * LPES1 is not supported (old iSeries mode) + * + * On anything else, we behave as if LPES0 is 1 + * (externals don't alter MSR:HV) + */ +#if defined(TARGET_PPC64) + if (excp_model == POWERPC_EXCP_POWER7 || + excp_model == POWERPC_EXCP_POWER8 || + excp_model == POWERPC_EXCP_POWER9 || + excp_model == POWERPC_EXCP_POWER10) { + lpes0 = !!(env->spr[SPR_LPCR] & LPCR_LPES0); + } else +#endif /* defined(TARGET_PPC64) */ + { + lpes0 = true; + } + + if (!lpes0) { + new_msr |= (target_ulong)MSR_HVB; + new_msr |= env->msr & ((target_ulong)1 << MSR_RI); + srr0 = SPR_HSRR0; + srr1 = SPR_HSRR1; + } + if (env->mpic_proxy) { + /* IACK the IRQ on delivery */ + env->spr[SPR_BOOKE_EPR] = ldl_phys(cs->as, env->mpic_iack); + } + break; + } + case POWERPC_EXCP_ALIGN: /* Alignment exception */ + /* Get rS/rD and rA from faulting opcode */ + /* + * Note: the opcode fields will not be set properly for a + * direct store load/store, but nobody cares as nobody + * actually uses direct store segments. + */ + env->spr[SPR_DSISR] |= (env->error_code & 0x03FF0000) >> 16; + break; + case POWERPC_EXCP_PROGRAM: /* Program exception */ + switch (env->error_code & ~0xF) { + case POWERPC_EXCP_FP: + if ((msr_fe0 == 0 && msr_fe1 == 0) || msr_fp == 0) { + trace_ppc_excp_fp_ignore(); + cs->exception_index = POWERPC_EXCP_NONE; + env->error_code = 0; + return; + } + + /* + * FP exceptions always have NIP pointing to the faulting + * instruction, so always use store_next and claim we are + * precise in the MSR. + */ + msr |= 0x00100000; + env->spr[SPR_BOOKE_ESR] = ESR_FP; + break; + case POWERPC_EXCP_INVAL: + trace_ppc_excp_inval(env->nip); + msr |= 0x00080000; + env->spr[SPR_BOOKE_ESR] = ESR_PIL; + break; + case POWERPC_EXCP_PRIV: + msr |= 0x00040000; + env->spr[SPR_BOOKE_ESR] = ESR_PPR; + break; + case POWERPC_EXCP_TRAP: + msr |= 0x00020000; + env->spr[SPR_BOOKE_ESR] = ESR_PTR; + break; + default: + /* Should never occur */ + cpu_abort(cs, "Invalid program exception %d. Aborting\n", + env->error_code); + break; + } + break; + case POWERPC_EXCP_SYSCALL: /* System call exception */ + lev = env->error_code; + + if ((lev == 1) && cpu->vhyp) { + dump_hcall(env); + } else { + dump_syscall(env); + } + + /* + * We need to correct the NIP which in this case is supposed + * to point to the next instruction + */ + env->nip += 4; + + /* "PAPR mode" built-in hypercall emulation */ + if ((lev == 1) && cpu->vhyp) { + PPCVirtualHypervisorClass *vhc = + PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp); + vhc->hypercall(cpu->vhyp, cpu); + return; + } + if (lev == 1) { + new_msr |= (target_ulong)MSR_HVB; + } + break; + case POWERPC_EXCP_SYSCALL_VECTORED: /* scv exception */ + lev = env->error_code; + dump_syscall(env); + env->nip += 4; + new_msr |= env->msr & ((target_ulong)1 << MSR_EE); + new_msr |= env->msr & ((target_ulong)1 << MSR_RI); + + vector += lev * 0x20; + + env->lr = env->nip; + env->ctr = msr; + break; + case POWERPC_EXCP_FPU: /* Floating-point unavailable exception */ + case POWERPC_EXCP_APU: /* Auxiliary processor unavailable */ + case POWERPC_EXCP_DECR: /* Decrementer exception */ + break; + case POWERPC_EXCP_FIT: /* Fixed-interval timer interrupt */ + /* FIT on 4xx */ + trace_ppc_excp_print("FIT"); + break; + case POWERPC_EXCP_WDT: /* Watchdog timer interrupt */ + trace_ppc_excp_print("WDT"); + switch (excp_model) { + case POWERPC_EXCP_BOOKE: + srr0 = SPR_BOOKE_CSRR0; + srr1 = SPR_BOOKE_CSRR1; + break; + default: + break; + } + break; + case POWERPC_EXCP_DTLB: /* Data TLB error */ + case POWERPC_EXCP_ITLB: /* Instruction TLB error */ + break; + case POWERPC_EXCP_DEBUG: /* Debug interrupt */ + if (env->flags & POWERPC_FLAG_DE) { + /* FIXME: choose one or the other based on CPU type */ + srr0 = SPR_BOOKE_DSRR0; + srr1 = SPR_BOOKE_DSRR1; + + env->spr[SPR_BOOKE_CSRR0] = env->nip; + env->spr[SPR_BOOKE_CSRR1] = msr; + + /* DBSR already modified by caller */ + } else { + cpu_abort(cs, "Debug exception triggered on unsupported model\n"); + } + break; + case POWERPC_EXCP_SPEU: /* SPE/embedded floating-point unavailable/VPU */ + env->spr[SPR_BOOKE_ESR] = ESR_SPV; + break; + case POWERPC_EXCP_DOORI: /* Embedded doorbell interrupt */ + break; + case POWERPC_EXCP_DOORCI: /* Embedded doorbell critical interrupt */ + srr0 = SPR_BOOKE_CSRR0; + srr1 = SPR_BOOKE_CSRR1; + break; + case POWERPC_EXCP_RESET: /* System reset exception */ + /* A power-saving exception sets ME, otherwise it is unchanged */ + if (msr_pow) { + /* indicate that we resumed from power save mode */ + msr |= 0x10000; + new_msr |= ((target_ulong)1 << MSR_ME); + } + if (env->msr_mask & MSR_HVB) { + /* + * ISA specifies HV, but can be delivered to guest with HV + * clear (e.g., see FWNMI in PAPR, NMI injection in QEMU). + */ + new_msr |= (target_ulong)MSR_HVB; + } else { + if (msr_pow) { + cpu_abort(cs, "Trying to deliver power-saving system reset " + "exception %d with no HV support\n", excp); + } + } + break; + case POWERPC_EXCP_DSEG: /* Data segment exception */ + case POWERPC_EXCP_ISEG: /* Instruction segment exception */ + case POWERPC_EXCP_TRACE: /* Trace exception */ + break; + case POWERPC_EXCP_HISI: /* Hypervisor instruction storage exception */ + msr |= env->error_code; + /* fall through */ + case POWERPC_EXCP_HDECR: /* Hypervisor decrementer exception */ + case POWERPC_EXCP_HDSI: /* Hypervisor data storage exception */ + case POWERPC_EXCP_HDSEG: /* Hypervisor data segment exception */ + case POWERPC_EXCP_HISEG: /* Hypervisor instruction segment exception */ + case POWERPC_EXCP_SDOOR_HV: /* Hypervisor Doorbell interrupt */ + case POWERPC_EXCP_HV_EMU: + case POWERPC_EXCP_HVIRT: /* Hypervisor virtualization */ + srr0 = SPR_HSRR0; + srr1 = SPR_HSRR1; + new_msr |= (target_ulong)MSR_HVB; + new_msr |= env->msr & ((target_ulong)1 << MSR_RI); + break; + case POWERPC_EXCP_VPU: /* Vector unavailable exception */ + case POWERPC_EXCP_VSXU: /* VSX unavailable exception */ + case POWERPC_EXCP_FU: /* Facility unavailable exception */ +#ifdef TARGET_PPC64 + env->spr[SPR_FSCR] |= ((target_ulong)env->error_code << 56); +#endif + break; + case POWERPC_EXCP_HV_FU: /* Hypervisor Facility Unavailable Exception */ +#ifdef TARGET_PPC64 + env->spr[SPR_HFSCR] |= ((target_ulong)env->error_code << FSCR_IC_POS); + srr0 = SPR_HSRR0; + srr1 = SPR_HSRR1; + new_msr |= (target_ulong)MSR_HVB; + new_msr |= env->msr & ((target_ulong)1 << MSR_RI); +#endif + break; + case POWERPC_EXCP_PIT: /* Programmable interval timer interrupt */ + trace_ppc_excp_print("PIT"); + break; + case POWERPC_EXCP_IFTLB: /* Instruction fetch TLB error */ + case POWERPC_EXCP_DLTLB: /* Data load TLB miss */ + case POWERPC_EXCP_DSTLB: /* Data store TLB miss */ + switch (excp_model) { + case POWERPC_EXCP_6xx: + /* Swap temporary saved registers with GPRs */ + if (!(new_msr & ((target_ulong)1 << MSR_TGPR))) { + new_msr |= (target_ulong)1 << MSR_TGPR; + hreg_swap_gpr_tgpr(env); + } + /* fall through */ + case POWERPC_EXCP_7xx: + ppc_excp_debug_sw_tlb(env, excp); + + msr |= env->crf[0] << 28; + msr |= env->error_code; /* key, D/I, S/L bits */ + /* Set way using a LRU mechanism */ + msr |= ((env->last_way + 1) & (env->nb_ways - 1)) << 17; + break; + default: + cpu_abort(cs, "Invalid TLB miss exception\n"); + break; + } + break; + case POWERPC_EXCP_EFPDI: /* Embedded floating-point data interrupt */ + case POWERPC_EXCP_EFPRI: /* Embedded floating-point round interrupt */ + case POWERPC_EXCP_EPERFM: /* Embedded performance monitor interrupt */ + case POWERPC_EXCP_FPA: /* Floating-point assist exception */ + case POWERPC_EXCP_DABR: /* Data address breakpoint */ + case POWERPC_EXCP_IABR: /* Instruction address breakpoint */ + case POWERPC_EXCP_SMI: /* System management interrupt */ + case POWERPC_EXCP_THERM: /* Thermal interrupt */ + case POWERPC_EXCP_PERFM: /* Embedded performance monitor interrupt */ + case POWERPC_EXCP_VPUA: /* Vector assist exception */ + case POWERPC_EXCP_SOFTP: /* Soft patch exception */ + case POWERPC_EXCP_MAINT: /* Maintenance exception */ + case POWERPC_EXCP_MEXTBR: /* Maskable external breakpoint */ + case POWERPC_EXCP_NMEXTBR: /* Non maskable external breakpoint */ + cpu_abort(cs, "%s exception not implemented\n", + powerpc_excp_name(excp)); + break; + default: + excp_invalid: + cpu_abort(cs, "Invalid PowerPC exception %d. Aborting\n", excp); + break; + } + + /* Sanity check */ + if (!(env->msr_mask & MSR_HVB)) { + if (new_msr & MSR_HVB) { + cpu_abort(cs, "Trying to deliver HV exception (MSR) %d with " + "no HV support\n", excp); + } + if (srr0 == SPR_HSRR0) { + cpu_abort(cs, "Trying to deliver HV exception (HSRR) %d with " + "no HV support\n", excp); + } + } + + /* + * Sort out endianness of interrupt, this differs depending on the + * CPU, the HV mode, etc... + */ + if (ppc_interrupts_little_endian(cpu, !!(new_msr & MSR_HVB))) { + new_msr |= (target_ulong)1 << MSR_LE; + } + +#if defined(TARGET_PPC64) + if (excp_model == POWERPC_EXCP_BOOKE) { + if (env->spr[SPR_BOOKE_EPCR] & EPCR_ICM) { + /* Cat.64-bit: EPCR.ICM is copied to MSR.CM */ + new_msr |= (target_ulong)1 << MSR_CM; + } else { + vector = (uint32_t)vector; + } + } else { + if (!msr_isf && !mmu_is_64bit(env->mmu_model)) { + vector = (uint32_t)vector; + } else { + new_msr |= (target_ulong)1 << MSR_SF; + } + } +#endif + + if (excp != POWERPC_EXCP_SYSCALL_VECTORED) { + /* Save PC */ + env->spr[srr0] = env->nip; + + /* Save MSR */ + env->spr[srr1] = msr; + } + + /* This can update new_msr and vector if AIL applies */ + ppc_excp_apply_ail(cpu, excp_model, excp, msr, &new_msr, &vector); + + powerpc_set_excp_state(cpu, vector, new_msr); +} + static void powerpc_excp_74xx(PowerPCCPU *cpu, int excp) { CPUState *cs = CPU(cpu); @@ -1942,6 +2408,9 @@ static void powerpc_excp(PowerPCCPU *cpu, int excp) case POWERPC_EXCP_6xx: powerpc_excp_6xx(cpu, excp); break; + case POWERPC_EXCP_7xx: + powerpc_excp_7xx(cpu, excp); + break; case POWERPC_EXCP_74xx: powerpc_excp_74xx(cpu, excp); break; From 93848d6a4c2f7f65553ec4583ed1f3fed621eecc Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Wed, 9 Feb 2022 09:08:56 +0100 Subject: [PATCH 356/460] target/ppc: Simplify powerpc_excp_7xx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Differences from the generic powerpc_excp code: - Not BookE, so some MSR bits are cleared at interrupt dispatch; - No MSR_HV; - No power saving states; - No Hypervisor Emulation Assistance; - Not 64 bits; - No System call vectored; - No Alternate Interrupt Location. Exceptions used: POWERPC_EXCP_ALIGN POWERPC_EXCP_DECR POWERPC_EXCP_DLTLB POWERPC_EXCP_DSI POWERPC_EXCP_DSTLB POWERPC_EXCP_EXTERNAL POWERPC_EXCP_FPU POWERPC_EXCP_IABR POWERPC_EXCP_IFTLB POWERPC_EXCP_ISI POWERPC_EXCP_MCHECK POWERPC_EXCP_PERFM POWERPC_EXCP_PROGRAM POWERPC_EXCP_RESET POWERPC_EXCP_SMI POWERPC_EXCP_SYSCALL POWERPC_EXCP_THERM POWERPC_EXCP_TRACE Signed-off-by: Fabiano Rosas Message-Id: <20220204173430.1457358-4-farosas@linux.ibm.com> Signed-off-by: Cédric Le Goater --- target/ppc/excp_helper.c | 185 ++------------------------------------- 1 file changed, 9 insertions(+), 176 deletions(-) diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index df96f620b2..358c3f6206 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -758,54 +758,26 @@ static void powerpc_excp_7xx(PowerPCCPU *cpu, int excp) excp, env->error_code); /* new srr1 value excluding must-be-zero bits */ - if (excp_model == POWERPC_EXCP_BOOKE) { - msr = env->msr; - } else { - msr = env->msr & ~0x783f0000ULL; - } + msr = env->msr & ~0x783f0000ULL; /* - * new interrupt handler msr preserves existing HV and ME unless + * new interrupt handler msr preserves existing ME unless * explicitly overriden */ - new_msr = env->msr & (((target_ulong)1 << MSR_ME) | MSR_HVB); + new_msr = env->msr & ((target_ulong)1 << MSR_ME); /* target registers */ srr0 = SPR_SRR0; srr1 = SPR_SRR1; - /* - * check for special resume at 0x100 from doze/nap/sleep/winkle on - * P7/P8/P9 - */ - if (env->resume_as_sreset) { - excp = powerpc_reset_wakeup(cs, env, excp, &msr); - } - /* * Hypervisor emulation assistance interrupt only exists on server - * arch 2.05 server or later. We also don't want to generate it if - * we don't have HVB in msr_mask (PAPR mode). + * arch 2.05 server or later. */ - if (excp == POWERPC_EXCP_HV_EMU -#if defined(TARGET_PPC64) - && !(mmu_is_64bit(env->mmu_model) && (env->msr_mask & MSR_HVB)) -#endif /* defined(TARGET_PPC64) */ - - ) { + if (excp == POWERPC_EXCP_HV_EMU) { excp = POWERPC_EXCP_PROGRAM; } -#ifdef TARGET_PPC64 - /* - * SPEU and VPU share the same IVOR but they exist in different - * processors. SPEU is e500v1/2 only and VPU is e6500 only. - */ - if (excp_model == POWERPC_EXCP_BOOKE && excp == POWERPC_EXCP_VPU) { - excp = POWERPC_EXCP_SPEU; - } -#endif - vector = env->excp_vectors[excp]; if (vector == (target_ulong)-1ULL) { cpu_abort(cs, "Raised an exception without defined vector %d\n", @@ -815,22 +787,6 @@ static void powerpc_excp_7xx(PowerPCCPU *cpu, int excp) vector |= env->excp_prefix; switch (excp) { - case POWERPC_EXCP_CRITICAL: /* Critical input */ - switch (excp_model) { - case POWERPC_EXCP_40x: - srr0 = SPR_40x_SRR2; - srr1 = SPR_40x_SRR3; - break; - case POWERPC_EXCP_BOOKE: - srr0 = SPR_BOOKE_CSRR0; - srr1 = SPR_BOOKE_CSRR1; - break; - case POWERPC_EXCP_6xx: - break; - default: - goto excp_invalid; - } - break; case POWERPC_EXCP_MCHECK: /* Machine check exception */ if (msr_me == 0) { /* @@ -994,63 +950,9 @@ static void powerpc_excp_7xx(PowerPCCPU *cpu, int excp) new_msr |= (target_ulong)MSR_HVB; } break; - case POWERPC_EXCP_SYSCALL_VECTORED: /* scv exception */ - lev = env->error_code; - dump_syscall(env); - env->nip += 4; - new_msr |= env->msr & ((target_ulong)1 << MSR_EE); - new_msr |= env->msr & ((target_ulong)1 << MSR_RI); - - vector += lev * 0x20; - - env->lr = env->nip; - env->ctr = msr; - break; case POWERPC_EXCP_FPU: /* Floating-point unavailable exception */ - case POWERPC_EXCP_APU: /* Auxiliary processor unavailable */ case POWERPC_EXCP_DECR: /* Decrementer exception */ break; - case POWERPC_EXCP_FIT: /* Fixed-interval timer interrupt */ - /* FIT on 4xx */ - trace_ppc_excp_print("FIT"); - break; - case POWERPC_EXCP_WDT: /* Watchdog timer interrupt */ - trace_ppc_excp_print("WDT"); - switch (excp_model) { - case POWERPC_EXCP_BOOKE: - srr0 = SPR_BOOKE_CSRR0; - srr1 = SPR_BOOKE_CSRR1; - break; - default: - break; - } - break; - case POWERPC_EXCP_DTLB: /* Data TLB error */ - case POWERPC_EXCP_ITLB: /* Instruction TLB error */ - break; - case POWERPC_EXCP_DEBUG: /* Debug interrupt */ - if (env->flags & POWERPC_FLAG_DE) { - /* FIXME: choose one or the other based on CPU type */ - srr0 = SPR_BOOKE_DSRR0; - srr1 = SPR_BOOKE_DSRR1; - - env->spr[SPR_BOOKE_CSRR0] = env->nip; - env->spr[SPR_BOOKE_CSRR1] = msr; - - /* DBSR already modified by caller */ - } else { - cpu_abort(cs, "Debug exception triggered on unsupported model\n"); - } - break; - case POWERPC_EXCP_SPEU: /* SPE/embedded floating-point unavailable/VPU */ - env->spr[SPR_BOOKE_ESR] = ESR_SPV; - break; - case POWERPC_EXCP_DOORI: /* Embedded doorbell interrupt */ - break; - case POWERPC_EXCP_DOORCI: /* Embedded doorbell critical interrupt */ - srr0 = SPR_BOOKE_CSRR0; - srr1 = SPR_BOOKE_CSRR1; - break; case POWERPC_EXCP_RESET: /* System reset exception */ /* A power-saving exception sets ME, otherwise it is unchanged */ if (msr_pow) { @@ -1071,44 +973,8 @@ static void powerpc_excp_7xx(PowerPCCPU *cpu, int excp) } } break; - case POWERPC_EXCP_DSEG: /* Data segment exception */ - case POWERPC_EXCP_ISEG: /* Instruction segment exception */ case POWERPC_EXCP_TRACE: /* Trace exception */ break; - case POWERPC_EXCP_HISI: /* Hypervisor instruction storage exception */ - msr |= env->error_code; - /* fall through */ - case POWERPC_EXCP_HDECR: /* Hypervisor decrementer exception */ - case POWERPC_EXCP_HDSI: /* Hypervisor data storage exception */ - case POWERPC_EXCP_HDSEG: /* Hypervisor data segment exception */ - case POWERPC_EXCP_HISEG: /* Hypervisor instruction segment exception */ - case POWERPC_EXCP_SDOOR_HV: /* Hypervisor Doorbell interrupt */ - case POWERPC_EXCP_HV_EMU: - case POWERPC_EXCP_HVIRT: /* Hypervisor virtualization */ - srr0 = SPR_HSRR0; - srr1 = SPR_HSRR1; - new_msr |= (target_ulong)MSR_HVB; - new_msr |= env->msr & ((target_ulong)1 << MSR_RI); - break; - case POWERPC_EXCP_VPU: /* Vector unavailable exception */ - case POWERPC_EXCP_VSXU: /* VSX unavailable exception */ - case POWERPC_EXCP_FU: /* Facility unavailable exception */ -#ifdef TARGET_PPC64 - env->spr[SPR_FSCR] |= ((target_ulong)env->error_code << 56); -#endif - break; - case POWERPC_EXCP_HV_FU: /* Hypervisor Facility Unavailable Exception */ -#ifdef TARGET_PPC64 - env->spr[SPR_HFSCR] |= ((target_ulong)env->error_code << FSCR_IC_POS); - srr0 = SPR_HSRR0; - srr1 = SPR_HSRR1; - new_msr |= (target_ulong)MSR_HVB; - new_msr |= env->msr & ((target_ulong)1 << MSR_RI); -#endif - break; - case POWERPC_EXCP_PIT: /* Programmable interval timer interrupt */ - trace_ppc_excp_print("PIT"); - break; case POWERPC_EXCP_IFTLB: /* Instruction fetch TLB error */ case POWERPC_EXCP_DLTLB: /* Data load TLB miss */ case POWERPC_EXCP_DSTLB: /* Data store TLB miss */ @@ -1133,25 +999,14 @@ static void powerpc_excp_7xx(PowerPCCPU *cpu, int excp) break; } break; - case POWERPC_EXCP_EFPDI: /* Embedded floating-point data interrupt */ - case POWERPC_EXCP_EFPRI: /* Embedded floating-point round interrupt */ - case POWERPC_EXCP_EPERFM: /* Embedded performance monitor interrupt */ - case POWERPC_EXCP_FPA: /* Floating-point assist exception */ - case POWERPC_EXCP_DABR: /* Data address breakpoint */ case POWERPC_EXCP_IABR: /* Instruction address breakpoint */ case POWERPC_EXCP_SMI: /* System management interrupt */ case POWERPC_EXCP_THERM: /* Thermal interrupt */ case POWERPC_EXCP_PERFM: /* Embedded performance monitor interrupt */ - case POWERPC_EXCP_VPUA: /* Vector assist exception */ - case POWERPC_EXCP_SOFTP: /* Soft patch exception */ - case POWERPC_EXCP_MAINT: /* Maintenance exception */ - case POWERPC_EXCP_MEXTBR: /* Maskable external breakpoint */ - case POWERPC_EXCP_NMEXTBR: /* Non maskable external breakpoint */ cpu_abort(cs, "%s exception not implemented\n", powerpc_excp_name(excp)); break; default: - excp_invalid: cpu_abort(cs, "Invalid PowerPC exception %d. Aborting\n", excp); break; } @@ -1176,33 +1031,11 @@ static void powerpc_excp_7xx(PowerPCCPU *cpu, int excp) new_msr |= (target_ulong)1 << MSR_LE; } -#if defined(TARGET_PPC64) - if (excp_model == POWERPC_EXCP_BOOKE) { - if (env->spr[SPR_BOOKE_EPCR] & EPCR_ICM) { - /* Cat.64-bit: EPCR.ICM is copied to MSR.CM */ - new_msr |= (target_ulong)1 << MSR_CM; - } else { - vector = (uint32_t)vector; - } - } else { - if (!msr_isf && !mmu_is_64bit(env->mmu_model)) { - vector = (uint32_t)vector; - } else { - new_msr |= (target_ulong)1 << MSR_SF; - } - } -#endif + /* Save PC */ + env->spr[srr0] = env->nip; - if (excp != POWERPC_EXCP_SYSCALL_VECTORED) { - /* Save PC */ - env->spr[srr0] = env->nip; - - /* Save MSR */ - env->spr[srr1] = msr; - } - - /* This can update new_msr and vector if AIL applies */ - ppc_excp_apply_ail(cpu, excp_model, excp, msr, &new_msr, &vector); + /* Save MSR */ + env->spr[srr1] = msr; powerpc_set_excp_state(cpu, vector, new_msr); } From 784f5a3403097a4427c91e7f62d257a3dbbf751e Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Wed, 9 Feb 2022 09:08:56 +0100 Subject: [PATCH 357/460] target/ppc: 7xx: Machine Check exception cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There's no MSR_HV in the 7xx. Also remove 40x and BookE code. Signed-off-by: Fabiano Rosas Message-Id: <20220204173430.1457358-5-farosas@linux.ibm.com> Signed-off-by: Cédric Le Goater --- target/ppc/excp_helper.c | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index 358c3f6206..4996b96616 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -802,34 +802,10 @@ static void powerpc_excp_7xx(PowerPCCPU *cpu, int excp) cs->halted = 1; cpu_interrupt_exittb(cs); } - if (env->msr_mask & MSR_HVB) { - /* - * ISA specifies HV, but can be delivered to guest with HV - * clear (e.g., see FWNMI in PAPR). - */ - new_msr |= (target_ulong)MSR_HVB; - } /* machine check exceptions don't have ME set */ new_msr &= ~((target_ulong)1 << MSR_ME); - /* XXX: should also have something loaded in DAR / DSISR */ - switch (excp_model) { - case POWERPC_EXCP_40x: - srr0 = SPR_40x_SRR2; - srr1 = SPR_40x_SRR3; - break; - case POWERPC_EXCP_BOOKE: - /* FIXME: choose one or the other based on CPU type */ - srr0 = SPR_BOOKE_MCSRR0; - srr1 = SPR_BOOKE_MCSRR1; - - env->spr[SPR_BOOKE_CSRR0] = env->nip; - env->spr[SPR_BOOKE_CSRR1] = msr; - break; - default: - break; - } break; case POWERPC_EXCP_DSI: /* Data storage exception */ trace_ppc_excp_dsi(env->spr[SPR_DSISR], env->spr[SPR_DAR]); From a53ce46537f7d6a8541ab91154a151f13601f102 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Wed, 9 Feb 2022 09:08:56 +0100 Subject: [PATCH 358/460] target/ppc: 7xx: External interrupt cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is no MSR_HV in the 7xx so remove the LPES0 handling. Signed-off-by: Fabiano Rosas Message-Id: <20220204173430.1457358-6-farosas@linux.ibm.com> Signed-off-by: Cédric Le Goater --- target/ppc/excp_helper.c | 37 ------------------------------------- 1 file changed, 37 deletions(-) diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index 4996b96616..5e2c2aa544 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -815,44 +815,7 @@ static void powerpc_excp_7xx(PowerPCCPU *cpu, int excp) msr |= env->error_code; break; case POWERPC_EXCP_EXTERNAL: /* External input */ - { - bool lpes0; - - cs = CPU(cpu); - - /* - * Exception targeting modifiers - * - * LPES0 is supported on POWER7/8/9 - * LPES1 is not supported (old iSeries mode) - * - * On anything else, we behave as if LPES0 is 1 - * (externals don't alter MSR:HV) - */ -#if defined(TARGET_PPC64) - if (excp_model == POWERPC_EXCP_POWER7 || - excp_model == POWERPC_EXCP_POWER8 || - excp_model == POWERPC_EXCP_POWER9 || - excp_model == POWERPC_EXCP_POWER10) { - lpes0 = !!(env->spr[SPR_LPCR] & LPCR_LPES0); - } else -#endif /* defined(TARGET_PPC64) */ - { - lpes0 = true; - } - - if (!lpes0) { - new_msr |= (target_ulong)MSR_HVB; - new_msr |= env->msr & ((target_ulong)1 << MSR_RI); - srr0 = SPR_HSRR0; - srr1 = SPR_HSRR1; - } - if (env->mpic_proxy) { - /* IACK the IRQ on delivery */ - env->spr[SPR_BOOKE_EPR] = ldl_phys(cs->as, env->mpic_iack); - } break; - } case POWERPC_EXCP_ALIGN: /* Alignment exception */ /* Get rS/rD and rA from faulting opcode */ /* From ab369390337d22d867f863cd368e8cf8b4c9fdde Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Wed, 9 Feb 2022 09:08:56 +0100 Subject: [PATCH 359/460] target/ppc: 7xx: Program exception cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There's no ESR in the 7xx. Signed-off-by: Fabiano Rosas Message-Id: <20220204173430.1457358-7-farosas@linux.ibm.com> Signed-off-by: Cédric Le Goater --- target/ppc/excp_helper.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index 5e2c2aa544..8f810f7de5 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -841,20 +841,16 @@ static void powerpc_excp_7xx(PowerPCCPU *cpu, int excp) * precise in the MSR. */ msr |= 0x00100000; - env->spr[SPR_BOOKE_ESR] = ESR_FP; break; case POWERPC_EXCP_INVAL: trace_ppc_excp_inval(env->nip); msr |= 0x00080000; - env->spr[SPR_BOOKE_ESR] = ESR_PIL; break; case POWERPC_EXCP_PRIV: msr |= 0x00040000; - env->spr[SPR_BOOKE_ESR] = ESR_PPR; break; case POWERPC_EXCP_TRAP: msr |= 0x00020000; - env->spr[SPR_BOOKE_ESR] = ESR_PTR; break; default: /* Should never occur */ From 3b57863593864ef35215ef7e210f06f4cabd73ed Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Wed, 9 Feb 2022 09:08:56 +0100 Subject: [PATCH 360/460] target/ppc: 7xx: System Call exception cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove the BookE code and add a comment explaining why we need to keep hypercall support even though this CPU does not have a hypervisor mode. Signed-off-by: Fabiano Rosas Message-Id: <20220204173430.1457358-8-farosas@linux.ibm.com> Signed-off-by: Cédric Le Goater --- target/ppc/excp_helper.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index 8f810f7de5..14a4eea9d6 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -747,7 +747,7 @@ static void powerpc_excp_7xx(PowerPCCPU *cpu, int excp) CPUPPCState *env = &cpu->env; int excp_model = env->excp_model; target_ulong msr, new_msr, vector; - int srr0, srr1, lev = -1; + int srr0, srr1; if (excp <= POWERPC_EXCP_NONE || excp >= POWERPC_EXCP_NB) { cpu_abort(cs, "Invalid PowerPC exception %d. Aborting\n", excp); @@ -860,9 +860,10 @@ static void powerpc_excp_7xx(PowerPCCPU *cpu, int excp) } break; case POWERPC_EXCP_SYSCALL: /* System call exception */ - lev = env->error_code; + { + int lev = env->error_code; - if ((lev == 1) && cpu->vhyp) { + if (lev == 1 && cpu->vhyp) { dump_hcall(env); } else { dump_syscall(env); @@ -874,17 +875,21 @@ static void powerpc_excp_7xx(PowerPCCPU *cpu, int excp) */ env->nip += 4; - /* "PAPR mode" built-in hypercall emulation */ - if ((lev == 1) && cpu->vhyp) { + /* + * The Virtual Open Firmware (VOF) relies on the 'sc 1' + * instruction to communicate with QEMU. The pegasos2 machine + * uses VOF and the 7xx CPUs, so although the 7xx don't have + * 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); return; } - if (lev == 1) { - new_msr |= (target_ulong)MSR_HVB; - } + break; + } case POWERPC_EXCP_FPU: /* Floating-point unavailable exception */ case POWERPC_EXCP_DECR: /* Decrementer exception */ break; From 3c3fa438f6c008e536c809c20530b423ff877b2f Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Wed, 9 Feb 2022 09:08:56 +0100 Subject: [PATCH 361/460] target/ppc: 7xx: System Reset cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Thre is no HV support in the 7xx. Signed-off-by: Fabiano Rosas Message-Id: <20220204173430.1457358-9-farosas@linux.ibm.com> Signed-off-by: Cédric Le Goater --- target/ppc/excp_helper.c | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index 14a4eea9d6..2bbfc25d2b 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -894,23 +894,9 @@ static void powerpc_excp_7xx(PowerPCCPU *cpu, int excp) case POWERPC_EXCP_DECR: /* Decrementer exception */ break; case POWERPC_EXCP_RESET: /* System reset exception */ - /* A power-saving exception sets ME, otherwise it is unchanged */ if (msr_pow) { - /* indicate that we resumed from power save mode */ - msr |= 0x10000; - new_msr |= ((target_ulong)1 << MSR_ME); - } - if (env->msr_mask & MSR_HVB) { - /* - * ISA specifies HV, but can be delivered to guest with HV - * clear (e.g., see FWNMI in PAPR, NMI injection in QEMU). - */ - new_msr |= (target_ulong)MSR_HVB; - } else { - if (msr_pow) { - cpu_abort(cs, "Trying to deliver power-saving system reset " - "exception %d with no HV support\n", excp); - } + cpu_abort(cs, "Trying to deliver power-saving system reset " + "exception %d with no HV support\n", excp); } break; case POWERPC_EXCP_TRACE: /* Trace exception */ From 7df40c5414b2f5e213fa30005f1600a429660cc5 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Wed, 9 Feb 2022 09:08:56 +0100 Subject: [PATCH 362/460] target/ppc: 7xx: Software TLB cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This code applies only to the 7xx CPUs, so we can remove the switch statement. Signed-off-by: Fabiano Rosas Message-Id: <20220204173430.1457358-10-farosas@linux.ibm.com> Signed-off-by: Cédric Le Goater --- target/ppc/excp_helper.c | 26 ++++++-------------------- 1 file changed, 6 insertions(+), 20 deletions(-) diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index 2bbfc25d2b..dd373a4d5b 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -745,7 +745,6 @@ static void powerpc_excp_7xx(PowerPCCPU *cpu, int excp) { CPUState *cs = CPU(cpu); CPUPPCState *env = &cpu->env; - int excp_model = env->excp_model; target_ulong msr, new_msr, vector; int srr0, srr1; @@ -904,26 +903,13 @@ static void powerpc_excp_7xx(PowerPCCPU *cpu, int excp) case POWERPC_EXCP_IFTLB: /* Instruction fetch TLB error */ case POWERPC_EXCP_DLTLB: /* Data load TLB miss */ case POWERPC_EXCP_DSTLB: /* Data store TLB miss */ - switch (excp_model) { - case POWERPC_EXCP_6xx: - /* Swap temporary saved registers with GPRs */ - if (!(new_msr & ((target_ulong)1 << MSR_TGPR))) { - new_msr |= (target_ulong)1 << MSR_TGPR; - hreg_swap_gpr_tgpr(env); - } - /* fall through */ - case POWERPC_EXCP_7xx: - ppc_excp_debug_sw_tlb(env, excp); + ppc_excp_debug_sw_tlb(env, excp); + + msr |= env->crf[0] << 28; + msr |= env->error_code; /* key, D/I, S/L bits */ + /* Set way using a LRU mechanism */ + msr |= ((env->last_way + 1) & (env->nb_ways - 1)) << 17; - msr |= env->crf[0] << 28; - msr |= env->error_code; /* key, D/I, S/L bits */ - /* Set way using a LRU mechanism */ - msr |= ((env->last_way + 1) & (env->nb_ways - 1)) << 17; - break; - default: - cpu_abort(cs, "Invalid TLB miss exception\n"); - break; - } break; case POWERPC_EXCP_IABR: /* Instruction address breakpoint */ case POWERPC_EXCP_SMI: /* System management interrupt */ From fe4b5c4c335c874ab05f3bdf40f2b62641d81c72 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Wed, 9 Feb 2022 09:08:56 +0100 Subject: [PATCH 363/460] target/ppc: 7xx: Set SRRs directly in exception code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 7xx CPUs don't have alternate/hypervisor Save and Restore Registers, so we can set SRR0 and SRR1 directly. Signed-off-by: Fabiano Rosas Message-Id: <20220204173430.1457358-11-farosas@linux.ibm.com> Signed-off-by: Cédric Le Goater --- target/ppc/excp_helper.c | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index dd373a4d5b..0eb6b7af55 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -746,7 +746,6 @@ static void powerpc_excp_7xx(PowerPCCPU *cpu, int excp) CPUState *cs = CPU(cpu); CPUPPCState *env = &cpu->env; target_ulong msr, new_msr, vector; - int srr0, srr1; if (excp <= POWERPC_EXCP_NONE || excp >= POWERPC_EXCP_NB) { cpu_abort(cs, "Invalid PowerPC exception %d. Aborting\n", excp); @@ -765,10 +764,6 @@ static void powerpc_excp_7xx(PowerPCCPU *cpu, int excp) */ new_msr = env->msr & ((target_ulong)1 << MSR_ME); - /* target registers */ - srr0 = SPR_SRR0; - srr1 = SPR_SRR1; - /* * Hypervisor emulation assistance interrupt only exists on server * arch 2.05 server or later. @@ -929,10 +924,6 @@ static void powerpc_excp_7xx(PowerPCCPU *cpu, int excp) cpu_abort(cs, "Trying to deliver HV exception (MSR) %d with " "no HV support\n", excp); } - if (srr0 == SPR_HSRR0) { - cpu_abort(cs, "Trying to deliver HV exception (HSRR) %d with " - "no HV support\n", excp); - } } /* @@ -944,10 +935,10 @@ static void powerpc_excp_7xx(PowerPCCPU *cpu, int excp) } /* Save PC */ - env->spr[srr0] = env->nip; + env->spr[SPR_SRR0] = env->nip; /* Save MSR */ - env->spr[srr1] = msr; + env->spr[SPR_SRR1] = msr; powerpc_set_excp_state(cpu, vector, new_msr); } From 2809137443030d89a0467e0c24db023c0951702a Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Wed, 9 Feb 2022 09:08:56 +0100 Subject: [PATCH 364/460] target/ppc: Remove powerpc_excp_legacy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that all CPU families have their own separate exception dispatching code we can remove powerpc_excp_legacy. Signed-off-by: Fabiano Rosas Reviewed-by: Cédric Le Goater Message-Id: <20220207183036.1507882-2-farosas@linux.ibm.com> Signed-off-by: Cédric Le Goater --- target/ppc/excp_helper.c | 477 +-------------------------------------- 1 file changed, 3 insertions(+), 474 deletions(-) diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index 0eb6b7af55..a393730c7b 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -163,7 +163,7 @@ static void ppc_excp_debug_sw_tlb(CPUPPCState *env, int excp) env->error_code); } - +#if defined(TARGET_PPC64) static int powerpc_reset_wakeup(CPUState *cs, CPUPPCState *env, int excp, target_ulong *msr) { @@ -267,7 +267,6 @@ static void ppc_excp_apply_ail(PowerPCCPU *cpu, int excp_model, int excp, target_ulong *new_msr, target_ulong *vector) { -#if defined(TARGET_PPC64) CPUPPCState *env = &cpu->env; bool mmu_all_on = ((msr >> MSR_IR) & 1) && ((msr >> MSR_DR) & 1); bool hv_escalation = !(msr & MSR_HVB) && (*new_msr & MSR_HVB); @@ -356,8 +355,8 @@ static void ppc_excp_apply_ail(PowerPCCPU *cpu, int excp_model, int excp, *vector |= 0xc000000000003000ull; /* Apply scv's AIL=3 offset */ } } -#endif } +#endif static void powerpc_set_excp_state(PowerPCCPU *cpu, target_ulong vector, target_ulong msr) @@ -1663,476 +1662,6 @@ static inline void powerpc_excp_books(PowerPCCPU *cpu, int excp) } #endif -/* - * Note that this function should be greatly optimized when called - * with a constant excp, from ppc_hw_interrupt - */ -static inline void powerpc_excp_legacy(PowerPCCPU *cpu, int excp) -{ - CPUState *cs = CPU(cpu); - CPUPPCState *env = &cpu->env; - int excp_model = env->excp_model; - target_ulong msr, new_msr, vector; - int srr0, srr1, lev = -1; - - if (excp <= POWERPC_EXCP_NONE || excp >= POWERPC_EXCP_NB) { - cpu_abort(cs, "Invalid PowerPC exception %d. Aborting\n", excp); - } - - qemu_log_mask(CPU_LOG_INT, "Raise exception at " TARGET_FMT_lx - " => %s (%d) error=%02x\n", env->nip, powerpc_excp_name(excp), - excp, env->error_code); - - /* new srr1 value excluding must-be-zero bits */ - if (excp_model == POWERPC_EXCP_BOOKE) { - msr = env->msr; - } else { - msr = env->msr & ~0x783f0000ULL; - } - - /* - * new interrupt handler msr preserves existing HV and ME unless - * explicitly overriden - */ - new_msr = env->msr & (((target_ulong)1 << MSR_ME) | MSR_HVB); - - /* target registers */ - srr0 = SPR_SRR0; - srr1 = SPR_SRR1; - - /* - * check for special resume at 0x100 from doze/nap/sleep/winkle on - * P7/P8/P9 - */ - if (env->resume_as_sreset) { - excp = powerpc_reset_wakeup(cs, env, excp, &msr); - } - - /* - * Hypervisor emulation assistance interrupt only exists on server - * arch 2.05 server or later. We also don't want to generate it if - * we don't have HVB in msr_mask (PAPR mode). - */ - if (excp == POWERPC_EXCP_HV_EMU -#if defined(TARGET_PPC64) - && !(mmu_is_64bit(env->mmu_model) && (env->msr_mask & MSR_HVB)) -#endif /* defined(TARGET_PPC64) */ - - ) { - excp = POWERPC_EXCP_PROGRAM; - } - -#ifdef TARGET_PPC64 - /* - * SPEU and VPU share the same IVOR but they exist in different - * processors. SPEU is e500v1/2 only and VPU is e6500 only. - */ - if (excp_model == POWERPC_EXCP_BOOKE && excp == POWERPC_EXCP_VPU) { - excp = POWERPC_EXCP_SPEU; - } -#endif - - vector = env->excp_vectors[excp]; - if (vector == (target_ulong)-1ULL) { - cpu_abort(cs, "Raised an exception without defined vector %d\n", - excp); - } - - vector |= env->excp_prefix; - - switch (excp) { - case POWERPC_EXCP_CRITICAL: /* Critical input */ - switch (excp_model) { - case POWERPC_EXCP_40x: - srr0 = SPR_40x_SRR2; - srr1 = SPR_40x_SRR3; - break; - case POWERPC_EXCP_BOOKE: - srr0 = SPR_BOOKE_CSRR0; - srr1 = SPR_BOOKE_CSRR1; - break; - case POWERPC_EXCP_6xx: - break; - default: - goto excp_invalid; - } - break; - case POWERPC_EXCP_MCHECK: /* Machine check exception */ - if (msr_me == 0) { - /* - * 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"); - } - cs->halted = 1; - cpu_interrupt_exittb(cs); - } - if (env->msr_mask & MSR_HVB) { - /* - * ISA specifies HV, but can be delivered to guest with HV - * clear (e.g., see FWNMI in PAPR). - */ - new_msr |= (target_ulong)MSR_HVB; - } - - /* machine check exceptions don't have ME set */ - new_msr &= ~((target_ulong)1 << MSR_ME); - - /* XXX: should also have something loaded in DAR / DSISR */ - switch (excp_model) { - case POWERPC_EXCP_40x: - srr0 = SPR_40x_SRR2; - srr1 = SPR_40x_SRR3; - break; - case POWERPC_EXCP_BOOKE: - /* FIXME: choose one or the other based on CPU type */ - srr0 = SPR_BOOKE_MCSRR0; - srr1 = SPR_BOOKE_MCSRR1; - - env->spr[SPR_BOOKE_CSRR0] = env->nip; - env->spr[SPR_BOOKE_CSRR1] = msr; - break; - default: - break; - } - break; - case POWERPC_EXCP_DSI: /* Data storage exception */ - trace_ppc_excp_dsi(env->spr[SPR_DSISR], env->spr[SPR_DAR]); - break; - case POWERPC_EXCP_ISI: /* Instruction storage exception */ - trace_ppc_excp_isi(msr, env->nip); - msr |= env->error_code; - break; - case POWERPC_EXCP_EXTERNAL: /* External input */ - { - bool lpes0; - - cs = CPU(cpu); - - /* - * Exception targeting modifiers - * - * LPES0 is supported on POWER7/8/9 - * LPES1 is not supported (old iSeries mode) - * - * On anything else, we behave as if LPES0 is 1 - * (externals don't alter MSR:HV) - */ -#if defined(TARGET_PPC64) - if (excp_model == POWERPC_EXCP_POWER7 || - excp_model == POWERPC_EXCP_POWER8 || - excp_model == POWERPC_EXCP_POWER9 || - excp_model == POWERPC_EXCP_POWER10) { - lpes0 = !!(env->spr[SPR_LPCR] & LPCR_LPES0); - } else -#endif /* defined(TARGET_PPC64) */ - { - lpes0 = true; - } - - if (!lpes0) { - new_msr |= (target_ulong)MSR_HVB; - new_msr |= env->msr & ((target_ulong)1 << MSR_RI); - srr0 = SPR_HSRR0; - srr1 = SPR_HSRR1; - } - if (env->mpic_proxy) { - /* IACK the IRQ on delivery */ - env->spr[SPR_BOOKE_EPR] = ldl_phys(cs->as, env->mpic_iack); - } - break; - } - case POWERPC_EXCP_ALIGN: /* Alignment exception */ - /* Get rS/rD and rA from faulting opcode */ - /* - * Note: the opcode fields will not be set properly for a - * direct store load/store, but nobody cares as nobody - * actually uses direct store segments. - */ - env->spr[SPR_DSISR] |= (env->error_code & 0x03FF0000) >> 16; - break; - case POWERPC_EXCP_PROGRAM: /* Program exception */ - switch (env->error_code & ~0xF) { - case POWERPC_EXCP_FP: - if ((msr_fe0 == 0 && msr_fe1 == 0) || msr_fp == 0) { - trace_ppc_excp_fp_ignore(); - cs->exception_index = POWERPC_EXCP_NONE; - env->error_code = 0; - return; - } - - /* - * FP exceptions always have NIP pointing to the faulting - * instruction, so always use store_next and claim we are - * precise in the MSR. - */ - msr |= 0x00100000; - env->spr[SPR_BOOKE_ESR] = ESR_FP; - break; - case POWERPC_EXCP_INVAL: - trace_ppc_excp_inval(env->nip); - msr |= 0x00080000; - env->spr[SPR_BOOKE_ESR] = ESR_PIL; - break; - case POWERPC_EXCP_PRIV: - msr |= 0x00040000; - env->spr[SPR_BOOKE_ESR] = ESR_PPR; - break; - case POWERPC_EXCP_TRAP: - msr |= 0x00020000; - env->spr[SPR_BOOKE_ESR] = ESR_PTR; - break; - default: - /* Should never occur */ - cpu_abort(cs, "Invalid program exception %d. Aborting\n", - env->error_code); - break; - } - break; - case POWERPC_EXCP_SYSCALL: /* System call exception */ - lev = env->error_code; - - if ((lev == 1) && cpu->vhyp) { - dump_hcall(env); - } else { - dump_syscall(env); - } - - /* - * We need to correct the NIP which in this case is supposed - * to point to the next instruction - */ - env->nip += 4; - - /* "PAPR mode" built-in hypercall emulation */ - if ((lev == 1) && cpu->vhyp) { - PPCVirtualHypervisorClass *vhc = - PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp); - vhc->hypercall(cpu->vhyp, cpu); - return; - } - if (lev == 1) { - new_msr |= (target_ulong)MSR_HVB; - } - break; - case POWERPC_EXCP_SYSCALL_VECTORED: /* scv exception */ - lev = env->error_code; - dump_syscall(env); - env->nip += 4; - new_msr |= env->msr & ((target_ulong)1 << MSR_EE); - new_msr |= env->msr & ((target_ulong)1 << MSR_RI); - - vector += lev * 0x20; - - env->lr = env->nip; - env->ctr = msr; - break; - case POWERPC_EXCP_FPU: /* Floating-point unavailable exception */ - case POWERPC_EXCP_APU: /* Auxiliary processor unavailable */ - case POWERPC_EXCP_DECR: /* Decrementer exception */ - break; - case POWERPC_EXCP_FIT: /* Fixed-interval timer interrupt */ - /* FIT on 4xx */ - trace_ppc_excp_print("FIT"); - break; - case POWERPC_EXCP_WDT: /* Watchdog timer interrupt */ - trace_ppc_excp_print("WDT"); - switch (excp_model) { - case POWERPC_EXCP_BOOKE: - srr0 = SPR_BOOKE_CSRR0; - srr1 = SPR_BOOKE_CSRR1; - break; - default: - break; - } - break; - case POWERPC_EXCP_DTLB: /* Data TLB error */ - case POWERPC_EXCP_ITLB: /* Instruction TLB error */ - break; - case POWERPC_EXCP_DEBUG: /* Debug interrupt */ - if (env->flags & POWERPC_FLAG_DE) { - /* FIXME: choose one or the other based on CPU type */ - srr0 = SPR_BOOKE_DSRR0; - srr1 = SPR_BOOKE_DSRR1; - - env->spr[SPR_BOOKE_CSRR0] = env->nip; - env->spr[SPR_BOOKE_CSRR1] = msr; - - /* DBSR already modified by caller */ - } else { - cpu_abort(cs, "Debug exception triggered on unsupported model\n"); - } - break; - case POWERPC_EXCP_SPEU: /* SPE/embedded floating-point unavailable/VPU */ - env->spr[SPR_BOOKE_ESR] = ESR_SPV; - break; - case POWERPC_EXCP_DOORI: /* Embedded doorbell interrupt */ - break; - case POWERPC_EXCP_DOORCI: /* Embedded doorbell critical interrupt */ - srr0 = SPR_BOOKE_CSRR0; - srr1 = SPR_BOOKE_CSRR1; - break; - case POWERPC_EXCP_RESET: /* System reset exception */ - /* A power-saving exception sets ME, otherwise it is unchanged */ - if (msr_pow) { - /* indicate that we resumed from power save mode */ - msr |= 0x10000; - new_msr |= ((target_ulong)1 << MSR_ME); - } - if (env->msr_mask & MSR_HVB) { - /* - * ISA specifies HV, but can be delivered to guest with HV - * clear (e.g., see FWNMI in PAPR, NMI injection in QEMU). - */ - new_msr |= (target_ulong)MSR_HVB; - } else { - if (msr_pow) { - cpu_abort(cs, "Trying to deliver power-saving system reset " - "exception %d with no HV support\n", excp); - } - } - break; - case POWERPC_EXCP_DSEG: /* Data segment exception */ - case POWERPC_EXCP_ISEG: /* Instruction segment exception */ - case POWERPC_EXCP_TRACE: /* Trace exception */ - break; - case POWERPC_EXCP_HISI: /* Hypervisor instruction storage exception */ - msr |= env->error_code; - /* fall through */ - case POWERPC_EXCP_HDECR: /* Hypervisor decrementer exception */ - case POWERPC_EXCP_HDSI: /* Hypervisor data storage exception */ - case POWERPC_EXCP_HDSEG: /* Hypervisor data segment exception */ - case POWERPC_EXCP_HISEG: /* Hypervisor instruction segment exception */ - case POWERPC_EXCP_SDOOR_HV: /* Hypervisor Doorbell interrupt */ - case POWERPC_EXCP_HV_EMU: - case POWERPC_EXCP_HVIRT: /* Hypervisor virtualization */ - srr0 = SPR_HSRR0; - srr1 = SPR_HSRR1; - new_msr |= (target_ulong)MSR_HVB; - new_msr |= env->msr & ((target_ulong)1 << MSR_RI); - break; - case POWERPC_EXCP_VPU: /* Vector unavailable exception */ - case POWERPC_EXCP_VSXU: /* VSX unavailable exception */ - case POWERPC_EXCP_FU: /* Facility unavailable exception */ -#ifdef TARGET_PPC64 - env->spr[SPR_FSCR] |= ((target_ulong)env->error_code << 56); -#endif - break; - case POWERPC_EXCP_HV_FU: /* Hypervisor Facility Unavailable Exception */ -#ifdef TARGET_PPC64 - env->spr[SPR_HFSCR] |= ((target_ulong)env->error_code << FSCR_IC_POS); - srr0 = SPR_HSRR0; - srr1 = SPR_HSRR1; - new_msr |= (target_ulong)MSR_HVB; - new_msr |= env->msr & ((target_ulong)1 << MSR_RI); -#endif - break; - case POWERPC_EXCP_PIT: /* Programmable interval timer interrupt */ - trace_ppc_excp_print("PIT"); - break; - case POWERPC_EXCP_IFTLB: /* Instruction fetch TLB error */ - case POWERPC_EXCP_DLTLB: /* Data load TLB miss */ - case POWERPC_EXCP_DSTLB: /* Data store TLB miss */ - switch (excp_model) { - case POWERPC_EXCP_6xx: - /* Swap temporary saved registers with GPRs */ - if (!(new_msr & ((target_ulong)1 << MSR_TGPR))) { - new_msr |= (target_ulong)1 << MSR_TGPR; - hreg_swap_gpr_tgpr(env); - } - /* fall through */ - case POWERPC_EXCP_7xx: - ppc_excp_debug_sw_tlb(env, excp); - - msr |= env->crf[0] << 28; - msr |= env->error_code; /* key, D/I, S/L bits */ - /* Set way using a LRU mechanism */ - msr |= ((env->last_way + 1) & (env->nb_ways - 1)) << 17; - break; - default: - cpu_abort(cs, "Invalid TLB miss exception\n"); - break; - } - break; - case POWERPC_EXCP_EFPDI: /* Embedded floating-point data interrupt */ - case POWERPC_EXCP_EFPRI: /* Embedded floating-point round interrupt */ - case POWERPC_EXCP_EPERFM: /* Embedded performance monitor interrupt */ - case POWERPC_EXCP_FPA: /* Floating-point assist exception */ - case POWERPC_EXCP_DABR: /* Data address breakpoint */ - case POWERPC_EXCP_IABR: /* Instruction address breakpoint */ - case POWERPC_EXCP_SMI: /* System management interrupt */ - case POWERPC_EXCP_THERM: /* Thermal interrupt */ - case POWERPC_EXCP_PERFM: /* Embedded performance monitor interrupt */ - case POWERPC_EXCP_VPUA: /* Vector assist exception */ - case POWERPC_EXCP_SOFTP: /* Soft patch exception */ - case POWERPC_EXCP_MAINT: /* Maintenance exception */ - case POWERPC_EXCP_MEXTBR: /* Maskable external breakpoint */ - case POWERPC_EXCP_NMEXTBR: /* Non maskable external breakpoint */ - cpu_abort(cs, "%s exception not implemented\n", - powerpc_excp_name(excp)); - break; - default: - excp_invalid: - cpu_abort(cs, "Invalid PowerPC exception %d. Aborting\n", excp); - break; - } - - /* Sanity check */ - if (!(env->msr_mask & MSR_HVB)) { - if (new_msr & MSR_HVB) { - cpu_abort(cs, "Trying to deliver HV exception (MSR) %d with " - "no HV support\n", excp); - } - if (srr0 == SPR_HSRR0) { - cpu_abort(cs, "Trying to deliver HV exception (HSRR) %d with " - "no HV support\n", excp); - } - } - - /* - * Sort out endianness of interrupt, this differs depending on the - * CPU, the HV mode, etc... - */ - if (ppc_interrupts_little_endian(cpu, !!(new_msr & MSR_HVB))) { - new_msr |= (target_ulong)1 << MSR_LE; - } - -#if defined(TARGET_PPC64) - if (excp_model == POWERPC_EXCP_BOOKE) { - if (env->spr[SPR_BOOKE_EPCR] & EPCR_ICM) { - /* Cat.64-bit: EPCR.ICM is copied to MSR.CM */ - new_msr |= (target_ulong)1 << MSR_CM; - } else { - vector = (uint32_t)vector; - } - } else { - if (!msr_isf && !mmu_is_64bit(env->mmu_model)) { - vector = (uint32_t)vector; - } else { - new_msr |= (target_ulong)1 << MSR_SF; - } - } -#endif - - if (excp != POWERPC_EXCP_SYSCALL_VECTORED) { - /* Save PC */ - env->spr[srr0] = env->nip; - - /* Save MSR */ - env->spr[srr1] = msr; - } - - /* This can update new_msr and vector if AIL applies */ - ppc_excp_apply_ail(cpu, excp_model, excp, msr, &new_msr, &vector); - - powerpc_set_excp_state(cpu, vector, new_msr); -} - static void powerpc_excp(PowerPCCPU *cpu, int excp) { CPUPPCState *env = &cpu->env; @@ -2161,7 +1690,7 @@ static void powerpc_excp(PowerPCCPU *cpu, int excp) powerpc_excp_books(cpu, excp); break; default: - powerpc_excp_legacy(cpu, excp); + g_assert_not_reached(); } } From c6eaac893af2baef30d3fcce790cbd89fee82a42 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Wed, 9 Feb 2022 09:08:56 +0100 Subject: [PATCH 365/460] target/ppc: powerpc_excp: Move common code to the caller function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make the cpu-specific powerpc_excp_* functions a bit simpler by moving the bounds check and logging to powerpc_excp. Signed-off-by: Fabiano Rosas Reviewed-by: Cédric Le Goater Message-Id: <20220207183036.1507882-3-farosas@linux.ibm.com> Signed-off-by: Cédric Le Goater --- target/ppc/excp_helper.c | 57 +++++++--------------------------------- 1 file changed, 9 insertions(+), 48 deletions(-) diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index a393730c7b..acdeeb4059 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -396,14 +396,6 @@ static void powerpc_excp_40x(PowerPCCPU *cpu, int excp) target_ulong msr, new_msr, vector; int srr0, srr1; - if (excp <= POWERPC_EXCP_NONE || excp >= POWERPC_EXCP_NB) { - cpu_abort(cs, "Invalid PowerPC exception %d. Aborting\n", excp); - } - - qemu_log_mask(CPU_LOG_INT, "Raise exception at " TARGET_FMT_lx - " => %s (%d) error=%02x\n", env->nip, powerpc_excp_name(excp), - excp, env->error_code); - /* new srr1 value excluding must-be-zero bits */ msr = env->msr & ~0x783f0000ULL; @@ -554,14 +546,6 @@ static void powerpc_excp_6xx(PowerPCCPU *cpu, int excp) CPUPPCState *env = &cpu->env; target_ulong msr, new_msr, vector; - if (excp <= POWERPC_EXCP_NONE || excp >= POWERPC_EXCP_NB) { - cpu_abort(cs, "Invalid PowerPC exception %d. Aborting\n", excp); - } - - qemu_log_mask(CPU_LOG_INT, "Raise exception at " TARGET_FMT_lx - " => %s (%d) error=%02x\n", env->nip, powerpc_excp_name(excp), - excp, env->error_code); - /* new srr1 value excluding must-be-zero bits */ msr = env->msr & ~0x783f0000ULL; @@ -746,14 +730,6 @@ static void powerpc_excp_7xx(PowerPCCPU *cpu, int excp) CPUPPCState *env = &cpu->env; target_ulong msr, new_msr, vector; - if (excp <= POWERPC_EXCP_NONE || excp >= POWERPC_EXCP_NB) { - cpu_abort(cs, "Invalid PowerPC exception %d. Aborting\n", excp); - } - - qemu_log_mask(CPU_LOG_INT, "Raise exception at " TARGET_FMT_lx - " => %s (%d) error=%02x\n", env->nip, powerpc_excp_name(excp), - excp, env->error_code); - /* new srr1 value excluding must-be-zero bits */ msr = env->msr & ~0x783f0000ULL; @@ -948,14 +924,6 @@ static void powerpc_excp_74xx(PowerPCCPU *cpu, int excp) CPUPPCState *env = &cpu->env; target_ulong msr, new_msr, vector; - if (excp <= POWERPC_EXCP_NONE || excp >= POWERPC_EXCP_NB) { - cpu_abort(cs, "Invalid PowerPC exception %d. Aborting\n", excp); - } - - qemu_log_mask(CPU_LOG_INT, "Raise exception at " TARGET_FMT_lx - " => %s (%d) error=%02x\n", env->nip, powerpc_excp_name(excp), - excp, env->error_code); - /* new srr1 value excluding must-be-zero bits */ msr = env->msr & ~0x783f0000ULL; @@ -1143,14 +1111,6 @@ static void powerpc_excp_booke(PowerPCCPU *cpu, int excp) target_ulong msr, new_msr, vector; int srr0, srr1; - if (excp <= POWERPC_EXCP_NONE || excp >= POWERPC_EXCP_NB) { - cpu_abort(cs, "Invalid PowerPC exception %d. Aborting\n", excp); - } - - qemu_log_mask(CPU_LOG_INT, "Raise exception at " TARGET_FMT_lx - " => %s (%d) error=%02x\n", env->nip, powerpc_excp_name(excp), - excp, env->error_code); - msr = env->msr; /* @@ -1370,14 +1330,6 @@ static void powerpc_excp_books(PowerPCCPU *cpu, int excp) target_ulong msr, new_msr, vector; int srr0, srr1, lev = -1; - if (excp <= POWERPC_EXCP_NONE || excp >= POWERPC_EXCP_NB) { - cpu_abort(cs, "Invalid PowerPC exception %d. Aborting\n", excp); - } - - qemu_log_mask(CPU_LOG_INT, "Raise exception at " TARGET_FMT_lx - " => %s (%d) error=%02x\n", env->nip, powerpc_excp_name(excp), - excp, env->error_code); - /* new srr1 value excluding must-be-zero bits */ msr = env->msr & ~0x783f0000ULL; @@ -1664,8 +1616,17 @@ static inline void powerpc_excp_books(PowerPCCPU *cpu, int excp) static void powerpc_excp(PowerPCCPU *cpu, int excp) { + CPUState *cs = CPU(cpu); CPUPPCState *env = &cpu->env; + if (excp <= POWERPC_EXCP_NONE || excp >= POWERPC_EXCP_NB) { + cpu_abort(cs, "Invalid PowerPC exception %d. Aborting\n", excp); + } + + qemu_log_mask(CPU_LOG_INT, "Raise exception at " TARGET_FMT_lx + " => %s (%d) error=%02x\n", env->nip, powerpc_excp_name(excp), + excp, env->error_code); + switch (env->excp_model) { case POWERPC_EXCP_40x: powerpc_excp_40x(cpu, excp); From fce9fbafe910822be093abfbf5fa259d5931aa66 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Wed, 9 Feb 2022 09:08:56 +0100 Subject: [PATCH 366/460] target/ppc: Assert if MSR bits differ from msr_mask during exceptions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We currently abort QEMU during the dispatch of an interrupt if we try to set MSR_HV without having MSR_HVB in the msr_mask. I think we should verify this for all MSR bits. There is no reason to ever have a MSR bit set if the corresponding bit is not set in that CPU's msr_mask. Note that this is not about the emulated code setting reserved bits. We clear the new_msr when starting to dispatch an exception, so if we end up with bits not present in the msr_mask that is a QEMU programming error. I kept the HSRR verification for BookS because it is the only CPU family that has HSRRs. Signed-off-by: Fabiano Rosas Message-Id: <20220207183036.1507882-4-farosas@linux.ibm.com> Signed-off-by: Cédric Le Goater --- target/ppc/excp_helper.c | 64 ++++------------------------------------ 1 file changed, 6 insertions(+), 58 deletions(-) diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index acdeeb4059..83be6698df 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -364,6 +364,8 @@ static void powerpc_set_excp_state(PowerPCCPU *cpu, CPUState *cs = CPU(cpu); CPUPPCState *env = &cpu->env; + assert((msr & env->msr_mask) == msr); + /* * We don't use hreg_store_msr here as already have treated any * special case that could occur. Just store MSR and update hflags @@ -372,7 +374,7 @@ static void powerpc_set_excp_state(PowerPCCPU *cpu, * will prevent setting of the HV bit which some exceptions might need * to do. */ - env->msr = msr & env->msr_mask; + env->msr = msr; hreg_compute_hflags(env); env->nip = vector; /* Reset exception state */ @@ -519,18 +521,6 @@ static void powerpc_excp_40x(PowerPCCPU *cpu, int excp) break; } - /* Sanity check */ - if (!(env->msr_mask & MSR_HVB)) { - if (new_msr & MSR_HVB) { - cpu_abort(cs, "Trying to deliver HV exception (MSR) %d with " - "no HV support\n", excp); - } - if (srr0 == SPR_HSRR0) { - cpu_abort(cs, "Trying to deliver HV exception (HSRR) %d with " - "no HV support\n", excp); - } - } - /* Save PC */ env->spr[srr0] = env->nip; @@ -699,14 +689,6 @@ static void powerpc_excp_6xx(PowerPCCPU *cpu, int excp) break; } - /* Sanity check */ - if (!(env->msr_mask & MSR_HVB)) { - if (new_msr & MSR_HVB) { - cpu_abort(cs, "Trying to deliver HV exception (MSR) %d with " - "no HV support\n", excp); - } - } - /* * Sort out endianness of interrupt, this differs depending on the * CPU, the HV mode, etc... @@ -893,14 +875,6 @@ static void powerpc_excp_7xx(PowerPCCPU *cpu, int excp) break; } - /* Sanity check */ - if (!(env->msr_mask & MSR_HVB)) { - if (new_msr & MSR_HVB) { - cpu_abort(cs, "Trying to deliver HV exception (MSR) %d with " - "no HV support\n", excp); - } - } - /* * Sort out endianness of interrupt, this differs depending on the * CPU, the HV mode, etc... @@ -1079,14 +1053,6 @@ static void powerpc_excp_74xx(PowerPCCPU *cpu, int excp) break; } - /* Sanity check */ - if (!(env->msr_mask & MSR_HVB)) { - if (new_msr & MSR_HVB) { - cpu_abort(cs, "Trying to deliver HV exception (MSR) %d with " - "no HV support\n", excp); - } - } - /* * Sort out endianness of interrupt, this differs depending on the * CPU, the HV mode, etc... @@ -1291,18 +1257,6 @@ static void powerpc_excp_booke(PowerPCCPU *cpu, int excp) break; } - /* Sanity check */ - if (!(env->msr_mask & MSR_HVB)) { - if (new_msr & MSR_HVB) { - cpu_abort(cs, "Trying to deliver HV exception (MSR) %d with " - "no HV support\n", excp); - } - if (srr0 == SPR_HSRR0) { - cpu_abort(cs, "Trying to deliver HV exception (HSRR) %d with " - "no HV support\n", excp); - } - } - #if defined(TARGET_PPC64) if (env->spr[SPR_BOOKE_EPCR] & EPCR_ICM) { /* Cat.64-bit: EPCR.ICM is copied to MSR.CM */ @@ -1573,15 +1527,9 @@ static void powerpc_excp_books(PowerPCCPU *cpu, int excp) } /* Sanity check */ - if (!(env->msr_mask & MSR_HVB)) { - if (new_msr & MSR_HVB) { - cpu_abort(cs, "Trying to deliver HV exception (MSR) %d with " - "no HV support\n", excp); - } - if (srr0 == SPR_HSRR0) { - cpu_abort(cs, "Trying to deliver HV exception (HSRR) %d with " - "no HV support\n", excp); - } + if (!(env->msr_mask & MSR_HVB) && srr0 == SPR_HSRR0) { + cpu_abort(cs, "Trying to deliver HV exception (HSRR) %d with " + "no HV support\n", excp); } /* From 10895ab6f76beaf2d2b5b450167c5d5102c8c3af Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Wed, 9 Feb 2022 09:08:56 +0100 Subject: [PATCH 367/460] target/ppc: books: Remove excp_model argument from ppc_excp_apply_ail MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We don't really need to check for exception model while applying AIL. We can check the lpcr_mask for the presence of LPCR_AIL/LPCR_HAIL. This removes one more instance of passing the exception model ID around. Signed-off-by: Fabiano Rosas Message-Id: <20220207183036.1507882-5-farosas@linux.ibm.com> Signed-off-by: Cédric Le Goater --- target/ppc/excp_helper.c | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index 83be6698df..fcc83a7701 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -262,11 +262,10 @@ static int powerpc_reset_wakeup(CPUState *cs, CPUPPCState *env, int excp, * | a | h | 11 | 1 | 1 | h | * +--------------------------------------------------------------------+ */ -static void ppc_excp_apply_ail(PowerPCCPU *cpu, int excp_model, int excp, - target_ulong msr, - target_ulong *new_msr, - target_ulong *vector) +static void ppc_excp_apply_ail(PowerPCCPU *cpu, int excp, target_ulong msr, + target_ulong *new_msr, target_ulong *vector) { + PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); CPUPPCState *env = &cpu->env; bool mmu_all_on = ((msr >> MSR_IR) & 1) && ((msr >> MSR_DR) & 1); bool hv_escalation = !(msr & MSR_HVB) && (*new_msr & MSR_HVB); @@ -279,8 +278,13 @@ static void ppc_excp_apply_ail(PowerPCCPU *cpu, int excp_model, int excp, return; } - if (excp_model == POWERPC_EXCP_POWER8 || - excp_model == POWERPC_EXCP_POWER9) { + if (!(pcc->lpcr_mask & LPCR_AIL)) { + /* This CPU does not have AIL */ + return; + } + + /* P8 & P9 */ + if (!(pcc->lpcr_mask & LPCR_HAIL)) { if (!mmu_all_on) { /* AIL only works if MSR[IR] and MSR[DR] are both enabled. */ return; @@ -303,7 +307,8 @@ static void ppc_excp_apply_ail(PowerPCCPU *cpu, int excp_model, int excp, return; } - } else if (excp_model == POWERPC_EXCP_POWER10) { + /* P10 and up */ + } else { if (!mmu_all_on && !hv_escalation) { /* * AIL works for HV interrupts even with guest MSR[IR/DR] disabled. @@ -328,9 +333,6 @@ static void ppc_excp_apply_ail(PowerPCCPU *cpu, int excp_model, int excp, /* AIL=1 and AIL=2 are reserved, treat them like AIL=0 */ return; } - } else { - /* Other processors do not support AIL */ - return; } /* @@ -1280,7 +1282,6 @@ static void powerpc_excp_books(PowerPCCPU *cpu, int excp) { CPUState *cs = CPU(cpu); CPUPPCState *env = &cpu->env; - int excp_model = env->excp_model; target_ulong msr, new_msr, vector; int srr0, srr1, lev = -1; @@ -1551,7 +1552,7 @@ static void powerpc_excp_books(PowerPCCPU *cpu, int excp) } /* This can update new_msr and vector if AIL applies */ - ppc_excp_apply_ail(cpu, excp_model, excp, msr, &new_msr, &vector); + ppc_excp_apply_ail(cpu, excp, msr, &new_msr, &vector); powerpc_set_excp_state(cpu, vector, new_msr); } From 205eb5a89e06f790831b7f6903c92e0dc78b6805 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Colombo?= Date: Wed, 9 Feb 2022 09:08:56 +0100 Subject: [PATCH 368/460] target/ppc: Change VSX instructions behavior to fill with zeros MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ISA v3.1 changed some VSX instructions behavior by changing what the other words/doubleword in the result should contain when the result is only one word/doubleword. e.g. xsmaxdp operates on doubleword 0 and saves the result also in doubleword 0. Before, the second doubleword result was undefined according to the ISA, but now it's stated that it should be zeroed. Even tough the result was undefined before, hardware implementing these instructions already filled these fields with 0s. Changing every ISA version in QEMU to this behavior makes the results match what happens in hardware. Signed-off-by: Víctor Colombo Reviewed-by: Richard Henderson Message-Id: <20220204181944.65063-1-victor.colombo@eldorado.org.br> Signed-off-by: Cédric Le Goater --- target/ppc/fpu_helper.c | 26 +++++++++++++------------- target/ppc/translate/vsx-impl.c.inc | 4 +++- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/target/ppc/fpu_helper.c b/target/ppc/fpu_helper.c index e5c29b53b8..bd76bee7f1 100644 --- a/target/ppc/fpu_helper.c +++ b/target/ppc/fpu_helper.c @@ -1696,7 +1696,7 @@ uint32_t helper_efdcmpeq(CPUPPCState *env, uint64_t op1, uint64_t op2) void helper_##name(CPUPPCState *env, ppc_vsr_t *xt, \ ppc_vsr_t *xa, ppc_vsr_t *xb) \ { \ - ppc_vsr_t t = *xt; \ + ppc_vsr_t t = { }; \ int i; \ \ helper_reset_fpstatus(env); \ @@ -1772,7 +1772,7 @@ void helper_xsaddqp(CPUPPCState *env, uint32_t opcode, void helper_##op(CPUPPCState *env, ppc_vsr_t *xt, \ ppc_vsr_t *xa, ppc_vsr_t *xb) \ { \ - ppc_vsr_t t = *xt; \ + ppc_vsr_t t = { }; \ int i; \ \ helper_reset_fpstatus(env); \ @@ -1843,7 +1843,7 @@ void helper_xsmulqp(CPUPPCState *env, uint32_t opcode, void helper_##op(CPUPPCState *env, ppc_vsr_t *xt, \ ppc_vsr_t *xa, ppc_vsr_t *xb) \ { \ - ppc_vsr_t t = *xt; \ + ppc_vsr_t t = { }; \ int i; \ \ helper_reset_fpstatus(env); \ @@ -1919,7 +1919,7 @@ void helper_xsdivqp(CPUPPCState *env, uint32_t opcode, #define VSX_RE(op, nels, tp, fld, sfprf, r2sp) \ void helper_##op(CPUPPCState *env, ppc_vsr_t *xt, ppc_vsr_t *xb) \ { \ - ppc_vsr_t t = *xt; \ + ppc_vsr_t t = { }; \ int i; \ \ helper_reset_fpstatus(env); \ @@ -1959,7 +1959,7 @@ VSX_RE(xvresp, 4, float32, VsrW(i), 0, 0) #define VSX_SQRT(op, nels, tp, fld, sfprf, r2sp) \ void helper_##op(CPUPPCState *env, ppc_vsr_t *xt, ppc_vsr_t *xb) \ { \ - ppc_vsr_t t = *xt; \ + ppc_vsr_t t = { }; \ int i; \ \ helper_reset_fpstatus(env); \ @@ -2004,7 +2004,7 @@ VSX_SQRT(xvsqrtsp, 4, float32, VsrW(i), 0, 0) #define VSX_RSQRTE(op, nels, tp, fld, sfprf, r2sp) \ void helper_##op(CPUPPCState *env, ppc_vsr_t *xt, ppc_vsr_t *xb) \ { \ - ppc_vsr_t t = *xt; \ + ppc_vsr_t t = { }; \ int i; \ \ helper_reset_fpstatus(env); \ @@ -2472,7 +2472,7 @@ void helper_xscmpuqp(CPUPPCState *env, uint32_t opcode, ppc_vsr_t *xa, void helper_##name(CPUPPCState *env, ppc_vsr_t *xt, \ ppc_vsr_t *xa, ppc_vsr_t *xb) \ { \ - ppc_vsr_t t = *xt; \ + ppc_vsr_t t = { }; \ int i; \ \ for (i = 0; i < nels; i++) { \ @@ -2498,7 +2498,7 @@ VSX_MAX_MIN(xvminsp, minnum, 4, float32, VsrW(i)) void helper_##name(CPUPPCState *env, \ ppc_vsr_t *xt, ppc_vsr_t *xa, ppc_vsr_t *xb) \ { \ - ppc_vsr_t t = *xt; \ + ppc_vsr_t t = { }; \ bool vxsnan_flag = false, vex_flag = false; \ \ if (unlikely(float64_is_any_nan(xa->VsrD(0)) || \ @@ -2533,7 +2533,7 @@ VSX_MAX_MINC(xsmincdp, 0); void helper_##name(CPUPPCState *env, \ ppc_vsr_t *xt, ppc_vsr_t *xa, ppc_vsr_t *xb) \ { \ - ppc_vsr_t t = *xt; \ + ppc_vsr_t t = { }; \ bool vxsnan_flag = false, vex_flag = false; \ \ if (unlikely(float64_is_any_nan(xa->VsrD(0)))) { \ @@ -2654,7 +2654,7 @@ VSX_CMP(xvcmpnesp, 4, float32, VsrW(i), eq, 0, 0) #define VSX_CVT_FP_TO_FP(op, nels, stp, ttp, sfld, tfld, sfprf) \ void helper_##op(CPUPPCState *env, ppc_vsr_t *xt, ppc_vsr_t *xb) \ { \ - ppc_vsr_t t = *xt; \ + ppc_vsr_t t = { }; \ int i; \ \ for (i = 0; i < nels; i++) { \ @@ -2833,7 +2833,7 @@ uint64_t helper_xscvspdpn(CPUPPCState *env, uint64_t xb) void helper_##op(CPUPPCState *env, ppc_vsr_t *xt, ppc_vsr_t *xb) \ { \ int all_flags = env->fp_status.float_exception_flags, flags; \ - ppc_vsr_t t = *xt; \ + ppc_vsr_t t = { }; \ int i; \ \ for (i = 0; i < nels; i++) { \ @@ -2917,7 +2917,7 @@ VSX_CVT_FP_TO_INT_VECTOR(xscvqpuwz, float128, uint32, f128, VsrD(0), 0x0ULL) #define VSX_CVT_INT_TO_FP(op, nels, stp, ttp, sfld, tfld, sfprf, r2sp) \ void helper_##op(CPUPPCState *env, ppc_vsr_t *xt, ppc_vsr_t *xb) \ { \ - ppc_vsr_t t = *xt; \ + ppc_vsr_t t = { }; \ int i; \ \ for (i = 0; i < nels; i++) { \ @@ -2990,7 +2990,7 @@ VSX_CVT_INT_TO_FP_VECTOR(xscvudqp, uint64, float128, VsrD(0), f128) #define VSX_ROUND(op, nels, tp, fld, rmode, sfprf) \ void helper_##op(CPUPPCState *env, ppc_vsr_t *xt, ppc_vsr_t *xb) \ { \ - ppc_vsr_t t = *xt; \ + ppc_vsr_t t = { }; \ int i; \ FloatRoundMode curr_rounding_mode; \ \ diff --git a/target/ppc/translate/vsx-impl.c.inc b/target/ppc/translate/vsx-impl.c.inc index c636e38164..128968b5e7 100644 --- a/target/ppc/translate/vsx-impl.c.inc +++ b/target/ppc/translate/vsx-impl.c.inc @@ -747,6 +747,7 @@ static void glue(gen_, name)(DisasContext *ctx) \ } \ } \ set_cpu_vsr(xT(ctx->opcode), xb, true); \ + set_cpu_vsr(xT(ctx->opcode), tcg_constant_i64(0), false); \ tcg_temp_free_i64(xb); \ tcg_temp_free_i64(sgm); \ } @@ -1073,6 +1074,7 @@ static void gen_##name(DisasContext *ctx) \ get_cpu_vsr(t0, xB(ctx->opcode), true); \ gen_helper_##name(t1, cpu_env, t0); \ set_cpu_vsr(xT(ctx->opcode), t1, true); \ + set_cpu_vsr(xT(ctx->opcode), tcg_constant_i64(0), false); \ tcg_temp_free_i64(t0); \ tcg_temp_free_i64(t1); \ } @@ -1700,7 +1702,7 @@ static void gen_xsiexpdp(DisasContext *ctx) tcg_gen_shli_i64(t0, t0, 52); tcg_gen_or_i64(xth, xth, t0); set_cpu_vsr(xT(ctx->opcode), xth, true); - /* dword[1] is undefined */ + set_cpu_vsr(xT(ctx->opcode), tcg_constant_i64(0), false); tcg_temp_free_i64(t0); tcg_temp_free_i64(xth); } From 96a46def58b3b7938d200fca6bd4916c3640d2f3 Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Wed, 9 Feb 2022 09:08:56 +0100 Subject: [PATCH 369/460] docs: rstfy confidential guest documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also rstfy the documentation for AMD SEV, and link it. The documentation for PEF had been merged into the pseries doc, fix the reference. Signed-off-by: Cornelia Huck Reviewed-by: Daniel Henrique Barboza Message-Id: <20220204161251.241877-1-cohuck@redhat.com> Signed-off-by: Cédric Le Goater --- MAINTAINERS | 2 +- .../confidential-guest-support.rst} | 15 +-- .../i386/amd-memory-encryption.rst} | 100 ++++++++++-------- docs/system/index.rst | 1 + docs/system/ppc/pseries.rst | 2 + docs/system/target-i386.rst | 1 + 6 files changed, 66 insertions(+), 55 deletions(-) rename docs/{confidential-guest-support.txt => system/confidential-guest-support.rst} (77%) rename docs/{amd-memory-encryption.txt => system/i386/amd-memory-encryption.rst} (62%) diff --git a/MAINTAINERS b/MAINTAINERS index 9814580975..8944fb561c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -408,7 +408,7 @@ M: Paolo Bonzini M: Marcelo Tosatti L: kvm@vger.kernel.org S: Supported -F: docs/amd-memory-encryption.txt +F: docs/system/i386/amd-memory-encryption.rst F: docs/system/i386/sgx.rst F: target/i386/kvm/ F: target/i386/sev* diff --git a/docs/confidential-guest-support.txt b/docs/system/confidential-guest-support.rst similarity index 77% rename from docs/confidential-guest-support.txt rename to docs/system/confidential-guest-support.rst index 71d07ba57a..0c490dbda2 100644 --- a/docs/confidential-guest-support.txt +++ b/docs/system/confidential-guest-support.rst @@ -19,10 +19,10 @@ Running a Confidential Guest To run a confidential guest you need to add two command line parameters: -1. Use "-object" to create a "confidential guest support" object. The +1. Use ``-object`` to create a "confidential guest support" object. The type and parameters will vary with the specific mechanism to be used -2. Set the "confidential-guest-support" machine parameter to the ID of +2. Set the ``confidential-guest-support`` machine parameter to the ID of the object from (1). Example (for AMD SEV):: @@ -37,13 +37,8 @@ Supported mechanisms Currently supported confidential guest mechanisms are: -AMD Secure Encrypted Virtualization (SEV) - docs/amd-memory-encryption.txt - -POWER Protected Execution Facility (PEF) - docs/papr-pef.txt - -s390x Protected Virtualization (PV) - docs/system/s390x/protvirt.rst +* AMD Secure Encrypted Virtualization (SEV) (see :doc:`i386/amd-memory-encryption`) +* POWER Protected Execution Facility (PEF) (see :ref:`power-papr-protected-execution-facility-pef`) +* s390x Protected Virtualization (PV) (see :doc:`s390x/protvirt`) Other mechanisms may be supported in future. diff --git a/docs/amd-memory-encryption.txt b/docs/system/i386/amd-memory-encryption.rst similarity index 62% rename from docs/amd-memory-encryption.txt rename to docs/system/i386/amd-memory-encryption.rst index ffca382b5f..215946f813 100644 --- a/docs/amd-memory-encryption.txt +++ b/docs/system/i386/amd-memory-encryption.rst @@ -1,3 +1,6 @@ +AMD Secure Encrypted Virtualization (SEV) +========================================= + Secure Encrypted Virtualization (SEV) is a feature found on AMD processors. SEV is an extension to the AMD-V architecture which supports running encrypted @@ -24,17 +27,18 @@ the hypervisor to satisfy the requested function. Launching --------- + 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, -LAUNCH_UPDATE_DATA, LAUNCH_MEASURE and LAUNCH_FINISH. These four commands +``MEMORY_ENCRYPT_OP`` ioctl provides commands to encrypt the images: ``LAUNCH_START``, +``LAUNCH_UPDATE_DATA``, ``LAUNCH_MEASURE`` and ``LAUNCH_FINISH``. These four commands together generate a fresh memory encryption key for the VM, encrypt the boot images and provide a measurement than can be used as an attestation of a successful launch. -For a SEV-ES guest, the LAUNCH_UPDATE_VMSA command is also used to encrypt the +For a SEV-ES guest, the ``LAUNCH_UPDATE_VMSA`` command is also used to encrypt the guest register state, or VM save area (VMSA), for all of the guest vCPUs. -LAUNCH_START is called first to create a cryptographic launch context within +``LAUNCH_START`` is called first to create a cryptographic launch context within the firmware. To create this context, guest owner must provide a guest policy, its public Diffie-Hellman key (PDH) and session parameters. These inputs should be treated as a binary blob and must be passed as-is to the SEV firmware. @@ -45,37 +49,37 @@ in bad measurement). The guest policy is a 4-byte data structure containing several flags that restricts what can be done on a running SEV guest. See KM Spec section 3 and 6.2 for more details. -The guest policy can be provided via the 'policy' property (see below) +The guest policy can be provided via the ``policy`` property:: -# ${QEMU} \ - sev-guest,id=sev0,policy=0x1...\ + # ${QEMU} \ + sev-guest,id=sev0,policy=0x1...\ Setting the "SEV-ES required" policy bit (bit 2) will launch the guest as a -SEV-ES guest (see below) +SEV-ES guest:: -# ${QEMU} \ - sev-guest,id=sev0,policy=0x5...\ + # ${QEMU} \ + sev-guest,id=sev0,policy=0x5...\ The guest owner provided DH certificate and session parameters will be used to establish a cryptographic session with the guest owner to negotiate keys used for the attestation. -The DH certificate and session blob can be provided via the 'dh-cert-file' and -'session-file' properties (see below) +The DH certificate and session blob can be provided via the ``dh-cert-file`` and +``session-file`` properties:: -# ${QEMU} \ - sev-guest,id=sev0,dh-cert-file=,session-file= + # ${QEMU} \ + sev-guest,id=sev0,dh-cert-file=,session-file= -LAUNCH_UPDATE_DATA encrypts the memory region using the cryptographic context -created via the LAUNCH_START command. If required, this command can be called +``LAUNCH_UPDATE_DATA`` encrypts the memory region using the cryptographic context +created via the ``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. -LAUNCH_UPDATE_VMSA encrypts all the vCPU VMSAs for a SEV-ES guest using the -cryptographic context created via the LAUNCH_START command. The command also +``LAUNCH_UPDATE_VMSA`` encrypts all the vCPU VMSAs for a SEV-ES guest using the +cryptographic context created via the ``LAUNCH_START`` command. The command also calculates the measurement of the VMSAs as it encrypts them. -LAUNCH_MEASURE can be used to retrieve the measurement of encrypted memory and, +``LAUNCH_MEASURE`` can be used to retrieve the measurement of encrypted memory and, for a SEV-ES guest, encrypted VMSAs. This measurement is a signature of the memory contents and, for a SEV-ES guest, the VMSA contents, that can be sent to the guest owner as an attestation that the memory and VMSAs were encrypted @@ -85,27 +89,28 @@ Since the guest owner knows the initial contents of the guest at boot, the attestation measurement can be verified by comparing it to what the guest owner expects. -LAUNCH_FINISH finalizes the guest launch and destroys the cryptographic +``LAUNCH_FINISH`` finalizes the guest launch and destroys the cryptographic context. -See SEV KM API Spec [1] 'Launching a guest' usage flow (Appendix A) for the +See SEV KM API Spec ([SEVKM]_) 'Launching a guest' usage flow (Appendix A) for the complete flow chart. -To launch a SEV guest +To launch a SEV guest:: -# ${QEMU} \ - -machine ...,confidential-guest-support=sev0 \ - -object sev-guest,id=sev0,cbitpos=47,reduced-phys-bits=1 + # ${QEMU} \ + -machine ...,confidential-guest-support=sev0 \ + -object sev-guest,id=sev0,cbitpos=47,reduced-phys-bits=1 -To launch a SEV-ES guest +To launch a SEV-ES guest:: -# ${QEMU} \ - -machine ...,confidential-guest-support=sev0 \ - -object sev-guest,id=sev0,cbitpos=47,reduced-phys-bits=1,policy=0x5 + # ${QEMU} \ + -machine ...,confidential-guest-support=sev0 \ + -object sev-guest,id=sev0,cbitpos=47,reduced-phys-bits=1,policy=0x5 An SEV-ES guest has some restrictions as compared to a SEV guest. Because the guest register state is encrypted and cannot be updated by the VMM/hypervisor, a SEV-ES guest: + - Does not support SMM - SMM support requires updating the guest register state. - Does not support reboot - a system reset requires updating the guest register @@ -114,35 +119,42 @@ a SEV-ES guest: manage booting APs. Debugging ------------ +--------- + Since the memory contents of a SEV guest are encrypted, hypervisor access to the guest memory will return cipher text. If the guest policy allows debugging, then a hypervisor can use the DEBUG_DECRYPT and DEBUG_ENCRYPT commands to access the guest memory region for debug purposes. This is not supported in QEMU yet. Snapshot/Restore ------------------ +---------------- + TODO Live Migration ----------------- +--------------- + TODO References ------------------ +---------- -AMD Memory Encryption whitepaper: -https://developer.amd.com/wordpress/media/2013/12/AMD_Memory_Encryption_Whitepaper_v7-Public.pdf +`AMD Memory Encryption whitepaper +`_ -Secure Encrypted Virtualization Key Management: -[1] http://developer.amd.com/wordpress/media/2017/11/55766_SEV-KM-API_Specification.pdf +.. [SEVKM] `Secure Encrypted Virtualization Key Management + `_ KVM Forum slides: -http://www.linux-kvm.org/images/7/74/02x08A-Thomas_Lendacky-AMDs_Virtualizatoin_Memory_Encryption_Technology.pdf -https://www.linux-kvm.org/images/9/94/Extending-Secure-Encrypted-Virtualization-with-SEV-ES-Thomas-Lendacky-AMD.pdf -AMD64 Architecture Programmer's Manual: - http://support.amd.com/TechDocs/24593.pdf - SME is section 7.10 - SEV is section 15.34 - SEV-ES is section 15.35 +* `AMD’s Virtualization Memory Encryption (2016) + `_ +* `Extending Secure Encrypted Virtualization With SEV-ES (2018) + `_ + +`AMD64 Architecture Programmer's Manual: +`_ + +* SME is section 7.10 +* SEV is section 15.34 +* SEV-ES is section 15.35 diff --git a/docs/system/index.rst b/docs/system/index.rst index 73bbedbc22..23e30e26e5 100644 --- a/docs/system/index.rst +++ b/docs/system/index.rst @@ -34,3 +34,4 @@ or Hypervisor.Framework. targets security multi-process + confidential-guest-support diff --git a/docs/system/ppc/pseries.rst b/docs/system/ppc/pseries.rst index 569237dc0c..d9b65ad4e8 100644 --- a/docs/system/ppc/pseries.rst +++ b/docs/system/ppc/pseries.rst @@ -224,6 +224,8 @@ nested. Combinations not shown in the table are not available. .. [3] Introduced on Power10 machines. +.. _power-papr-protected-execution-facility-pef: + POWER (PAPR) Protected Execution Facility (PEF) ----------------------------------------------- diff --git a/docs/system/target-i386.rst b/docs/system/target-i386.rst index 4daa53c35d..96bf54889a 100644 --- a/docs/system/target-i386.rst +++ b/docs/system/target-i386.rst @@ -28,6 +28,7 @@ Architectural features i386/cpu i386/kvm-pv i386/sgx + i386/amd-memory-encryption .. _pcsys_005freq: From 10717c26dbe1c138ba6af6d09a3bb9958d4fe3f2 Mon Sep 17 00:00:00 2001 From: Alexey Kardashevskiy Date: Wed, 9 Feb 2022 09:08:56 +0100 Subject: [PATCH 370/460] spapr/vof: Install rom and nvram binaries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This installs VOF-related binaries (the firmware and the preformatted NVRAM) as those were left out when the VOF was submitted initially. Fixes: fc8c745d5015 ("spapr: Implement Open Firmware client interface") Signed-off-by: Alexey Kardashevskiy Message-Id: <20220208103751.1587902-1-aik@ozlabs.ru> Signed-off-by: Cédric Le Goater --- pc-bios/meson.build | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pc-bios/meson.build b/pc-bios/meson.build index 4ac7a5509b..c86dedf7df 100644 --- a/pc-bios/meson.build +++ b/pc-bios/meson.build @@ -81,6 +81,8 @@ blobs = files( 'opensbi-riscv32-generic-fw_dynamic.bin', 'opensbi-riscv64-generic-fw_dynamic.bin', 'npcm7xx_bootrom.bin', + 'vof.bin', + 'vof-nvram.bin', ) if get_option('install_blobs') From 135e6a09a949e9dbf69003482561abe098c354e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 4 Feb 2022 20:43:10 +0000 Subject: [PATCH 371/460] tests/Makefile.include: clean-up old code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is no longer needed since a2ce7dbd91 ("meson: convert tests/qtest to meson", 2020-08-21) Signed-off-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20220204204335.1689602-2-alex.bennee@linaro.org> --- tests/Makefile.include | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tests/Makefile.include b/tests/Makefile.include index 9157a57b1a..646c8b1334 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -34,10 +34,6 @@ endif ifneq ($(wildcard config-host.mak),) export SRC_PATH -# Get the list of all supported sysemu targets -SYSEMU_TARGET_LIST := $(subst -softmmu.mak,,$(notdir \ - $(wildcard $(SRC_PATH)/configs/*-softmmu.mak))) - SPEED = quick # Build up our target list from the filtered list of ninja targets From 8dcb404bff6d9147765d7dd3e9c8493372186420 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 4 Feb 2022 20:43:11 +0000 Subject: [PATCH 372/460] tests/qtest: enable more vhost-user tests by default MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If this starts causing failures again we should probably fix that. Signed-off-by: Alex Bennée Tested-by: Thomas Huth Message-Id: <20220204204335.1689602-3-alex.bennee@linaro.org> --- tests/qtest/vhost-user-test.c | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/tests/qtest/vhost-user-test.c b/tests/qtest/vhost-user-test.c index 3d6337fb5c..2a4568cd7d 100644 --- a/tests/qtest/vhost-user-test.c +++ b/tests/qtest/vhost-user-test.c @@ -995,20 +995,17 @@ static void register_vhost_user_test(void) "virtio-net", test_migrate, &opts); - /* keeps failing on build-system since Aug 15 2017 */ - if (getenv("QTEST_VHOST_USER_FIXME")) { - opts.before = vhost_user_test_setup_reconnect; - qos_add_test("vhost-user/reconnect", "virtio-net", - test_reconnect, &opts); + opts.before = vhost_user_test_setup_reconnect; + qos_add_test("vhost-user/reconnect", "virtio-net", + test_reconnect, &opts); - opts.before = vhost_user_test_setup_connect_fail; - qos_add_test("vhost-user/connect-fail", "virtio-net", - test_vhost_user_started, &opts); + opts.before = vhost_user_test_setup_connect_fail; + qos_add_test("vhost-user/connect-fail", "virtio-net", + test_vhost_user_started, &opts); - opts.before = vhost_user_test_setup_flags_mismatch; - qos_add_test("vhost-user/flags-mismatch", "virtio-net", - test_vhost_user_started, &opts); - } + opts.before = vhost_user_test_setup_flags_mismatch; + qos_add_test("vhost-user/flags-mismatch", "virtio-net", + test_vhost_user_started, &opts); opts.before = vhost_user_test_setup_multiqueue; opts.edge.extra_device_opts = "mq=on"; From 029e2da880f323d97c644a9ee70150c15e91ae2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 4 Feb 2022 20:43:12 +0000 Subject: [PATCH 373/460] Makefile: also remove .gcno files when cleaning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Left over .gcno files from old builds can really confuse gcov and the user expects a clean slate after "make clean". Make clean mean clean. Signed-off-by: Alex Bennée Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20220204204335.1689602-4-alex.bennee@linaro.org> --- Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index db9a788601..e5fd1ebdf6 100644 --- a/Makefile +++ b/Makefile @@ -206,7 +206,8 @@ recurse-clean: $(addsuffix /clean, $(ROM_DIRS)) clean: recurse-clean -$(quiet-@)test -f build.ninja && $(NINJA) $(NINJAFLAGS) -t clean || : -$(quiet-@)test -f build.ninja && $(NINJA) $(NINJAFLAGS) clean-ctlist || : - find . \( -name '*.so' -o -name '*.dll' -o -name '*.[oda]' \) -type f \ + find . \( -name '*.so' -o -name '*.dll' -o \ + -name '*.[oda]' -o -name '*.gcno' \) -type f \ ! -path ./roms/edk2/ArmPkg/Library/GccLto/liblto-aarch64.a \ ! -path ./roms/edk2/ArmPkg/Library/GccLto/liblto-arm.a \ -exec rm {} + From 5934ebe99025e12dac39cd1ba91f4116aec32774 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 4 Feb 2022 20:43:13 +0000 Subject: [PATCH 374/460] .gitignore: add .gcov pattern MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The gcovr tool is very messy and can leave a lot of crap in the source tree even when using build directories. Signed-off-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20220204204335.1689602-5-alex.bennee@linaro.org> --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index eb2553026c..9726a778b3 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,4 @@ GTAGS *.depend_raw *.swp *.patch +*.gcov From 3260f4e6f1d211fb521dd6ee154f9ed650341cf7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 4 Feb 2022 20:43:14 +0000 Subject: [PATCH 375/460] MAINTAINERS: Cover lcitool submodule with build test / automation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit lcitool is used by build test / automation, we want maintainers to get notified if the submodule is updated. Reviewed-by: Daniel P. Berrangé Reviewed-by: Thomas Huth Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Alex Bennée Message-Id: <20220121154134.315047-2-f4bug@amsat.org> Message-Id: <20220204204335.1689602-6-alex.bennee@linaro.org> --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 9814580975..b0b845f445 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3585,6 +3585,7 @@ F: .travis.yml F: scripts/ci/ F: tests/docker/ F: tests/vm/ +F: tests/lcitool/ F: scripts/archive-source.sh W: https://gitlab.com/qemu-project/qemu/pipelines W: https://travis-ci.org/qemu/qemu From 9641ba6b58d2ecc82e87545069b223c4ac46476b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 4 Feb 2022 20:43:15 +0000 Subject: [PATCH 376/460] gitmodules: Correct libvirt-ci submodule URL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Correct the libvirt-ci repository URL to avoid this warning when cloning / refreshing the submodule: warning: redirecting to https://gitlab.com/libvirt/libvirt-ci.git/ Fixes: 4ebb040f1fd ("tests: integrate lcitool for generating build env manifests") Reviewed-by: Daniel P. Berrangé Reviewed-by: Thomas Huth Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Alex Bennée Message-Id: <20220121154134.315047-3-f4bug@amsat.org> Message-Id: <20220204204335.1689602-7-alex.bennee@linaro.org> --- .gitmodules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index 84425d87e2..f4b6a9b401 100644 --- a/.gitmodules +++ b/.gitmodules @@ -66,4 +66,4 @@ url = https://gitlab.com/qemu-project/vbootrom.git [submodule "tests/lcitool/libvirt-ci"] path = tests/lcitool/libvirt-ci - url = http://gitlab.com/libvirt/libvirt-ci + url = https://gitlab.com/libvirt/libvirt-ci.git From 5d3539d561d028263ea21e577fcadd12ff8abcc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 4 Feb 2022 20:43:16 +0000 Subject: [PATCH 377/460] tests/lcitool: Include local qemu.yml when refreshing cirrus-ci files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The script only include the local qemu.yml for Dockerfiles. Since we want to keep the Cirrus-CI generated files in sync, also use the --data-dir option in generate_cirrus(). Fixes: c45a540f4bd (".gitlab-ci.d/cirrus: auto-generate variables with lcitool") Reported-by: Daniel P. Berrangé Reviewed-by: Daniel P. Berrangé Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Alex Bennée Message-Id: <20220121154134.315047-4-f4bug@amsat.org> Message-Id: <20220204204335.1689602-8-alex.bennee@linaro.org> --- tests/lcitool/refresh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/lcitool/refresh b/tests/lcitool/refresh index 033120e223..25301f2ef0 100755 --- a/tests/lcitool/refresh +++ b/tests/lcitool/refresh @@ -62,7 +62,7 @@ def generate_dockerfile(host, target, cross=None, trailer=None): def generate_cirrus(target, trailer=None): filename = Path(src_dir, ".gitlab-ci.d", "cirrus", target + ".vars") - cmd = [lcitool_path, "variables", target, "qemu"] + cmd = lcitool_cmd + ["variables", target, "qemu"] generate(filename, cmd, trailer) ubuntu1804_skipssh = [ From 9e8be4c546ce8469ca9702715bf8f198d604b685 Mon Sep 17 00:00:00 2001 From: Michael Tokarev Date: Fri, 4 Feb 2022 20:43:17 +0000 Subject: [PATCH 378/460] drop libxml2 checks since libxml is not actually used (for parallels) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For a long time, we assumed that libxml2 is necessary for parallels block format support (block/parallels*). However, this format actually does not use libxml [*]. Since this is the only user of libxml2 in whole QEMU tree, we can drop all libxml2 checks and dependencies too. It is even more: --enable-parallels configure option was the only option which was silently ignored when it's (fake) dependency (libxml2) isn't installed. Drop all mentions of libxml2. [*] Actually the basis for libxml use were introduced in commit ed279a06c53 ("configure: add dependency") but the implementation was never merged: https://lore.kernel.org/qemu-devel/70227bbd-a517-70e9-714f-e6e0ec431be9@openvz.org/ Signed-off-by: Michael Tokarev Reviewed-by: Stefan Hajnoczi Message-Id: <20220119090423.149315-1-mjt@msgid.tls.msk.ru> Tested-by: Philippe Mathieu-Daudé Reviewed-by: Philippe Mathieu-Daudé [PMD: Updated description and adapted to use lcitool] Reviewed-by: Thomas Huth Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Alex Bennée Message-Id: <20220121154134.315047-5-f4bug@amsat.org> Reviewed-by: Vladimir Sementsov-Ogievskiy Message-Id: <20220204204335.1689602-9-alex.bennee@linaro.org> --- block/meson.build | 3 +-- meson.build | 6 ------ meson_options.txt | 2 -- scripts/checkpatch.pl | 1 - scripts/ci/org.centos/stream/8/x86_64/configure | 1 - scripts/coverity-scan/coverity-scan.docker | 1 - scripts/coverity-scan/run-coverity-scan | 2 +- scripts/meson-buildoptions.sh | 3 --- 8 files changed, 2 insertions(+), 17 deletions(-) diff --git a/block/meson.build b/block/meson.build index deb73ca389..90dc9983e5 100644 --- a/block/meson.build +++ b/block/meson.build @@ -58,8 +58,7 @@ block_ss.add(when: 'CONFIG_QED', if_true: files( 'qed-table.c', 'qed.c', )) -block_ss.add(when: [libxml2, 'CONFIG_PARALLELS'], - if_true: files('parallels.c', 'parallels-ext.c')) +block_ss.add(when: 'CONFIG_PARALLELS', if_true: files('parallels.c', 'parallels-ext.c')) block_ss.add(when: 'CONFIG_WIN32', if_true: files('file-win32.c', 'win32-aio.c')) block_ss.add(when: 'CONFIG_POSIX', if_true: [files('file-posix.c'), coref, iokit]) block_ss.add(when: libiscsi, if_true: files('iscsi-opts.c')) diff --git a/meson.build b/meson.build index 5f43355071..82db1e7e74 100644 --- a/meson.build +++ b/meson.build @@ -453,11 +453,6 @@ if not get_option('linux_io_uring').auto() or have_block required: get_option('linux_io_uring'), method: 'pkg-config', kwargs: static_kwargs) endif -libxml2 = not_found -if not get_option('libxml2').auto() or have_block - libxml2 = dependency('libxml-2.0', required: get_option('libxml2'), - method: 'pkg-config', kwargs: static_kwargs) -endif libnfs = not_found if not get_option('libnfs').auto() or have_block libnfs = dependency('libnfs', version: '>=1.9.3', @@ -3496,7 +3491,6 @@ summary_info += {'bzip2 support': libbzip2} summary_info += {'lzfse support': liblzfse} summary_info += {'zstd support': zstd} summary_info += {'NUMA host support': config_host.has_key('CONFIG_NUMA')} -summary_info += {'libxml2': libxml2} summary_info += {'capstone': capstone_opt == 'internal' ? capstone_opt : capstone} summary_info += {'libpmem support': libpmem} summary_info += {'libdaxctl support': libdaxctl} diff --git a/meson_options.txt b/meson_options.txt index 921967eddb..95d527f773 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -113,8 +113,6 @@ option('libudev', type : 'feature', value : 'auto', description: 'Use libudev to enumerate host devices') option('libusb', type : 'feature', value : 'auto', description: 'libusb support for USB passthrough') -option('libxml2', type : 'feature', value : 'auto', - description: 'libxml2 support for Parallels image format') option('linux_aio', type : 'feature', value : 'auto', description: 'Linux AIO support') option('linux_io_uring', type : 'feature', value : 'auto', diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 5caa739db4..5e50111060 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -307,7 +307,6 @@ our @typeList = ( qr{target_(?:u)?long}, qr{hwaddr}, # external libraries - qr{xml${Ident}}, qr{xen\w+_handle}, # Glib definitions qr{gchar}, diff --git a/scripts/ci/org.centos/stream/8/x86_64/configure b/scripts/ci/org.centos/stream/8/x86_64/configure index e05f2fddcc..9850dd4444 100755 --- a/scripts/ci/org.centos/stream/8/x86_64/configure +++ b/scripts/ci/org.centos/stream/8/x86_64/configure @@ -81,7 +81,6 @@ --disable-libssh \ --disable-libudev \ --disable-libusb \ ---disable-libxml2 \ --disable-linux-aio \ --disable-linux-io-uring \ --disable-linux-user \ diff --git a/scripts/coverity-scan/coverity-scan.docker b/scripts/coverity-scan/coverity-scan.docker index ecff6ac5b4..6f60a52d23 100644 --- a/scripts/coverity-scan/coverity-scan.docker +++ b/scripts/coverity-scan/coverity-scan.docker @@ -59,7 +59,6 @@ ENV PACKAGES \ libubsan \ libudev-devel \ libusbx-devel \ - libxml2-devel \ libzstd-devel \ llvm \ lzo-devel \ diff --git a/scripts/coverity-scan/run-coverity-scan b/scripts/coverity-scan/run-coverity-scan index 6d443250a9..181bdcb263 100755 --- a/scripts/coverity-scan/run-coverity-scan +++ b/scripts/coverity-scan/run-coverity-scan @@ -402,7 +402,7 @@ echo "Configuring..." --enable-libiscsi --enable-libnfs --enable-seccomp \ --enable-tpm --enable-libssh --enable-lzo --enable-snappy --enable-bzip2 \ --enable-numa --enable-rdma --enable-smartcard --enable-virglrenderer \ - --enable-mpath --enable-libxml2 --enable-glusterfs \ + --enable-mpath --enable-glusterfs \ --enable-virtfs --enable-zstd echo "Running cov-build..." diff --git a/scripts/meson-buildoptions.sh b/scripts/meson-buildoptions.sh index a4af02c527..48a454cece 100644 --- a/scripts/meson-buildoptions.sh +++ b/scripts/meson-buildoptions.sh @@ -58,7 +58,6 @@ meson_options_help() { printf "%s\n" ' libssh ssh block device support' printf "%s\n" ' libudev Use libudev to enumerate host devices' printf "%s\n" ' libusb libusb support for USB passthrough' - printf "%s\n" ' libxml2 libxml2 support for Parallels image format' printf "%s\n" ' linux-aio Linux AIO support' printf "%s\n" ' linux-io-uring Linux io_uring support' printf "%s\n" ' lzfse lzfse support for DMG images' @@ -188,8 +187,6 @@ _meson_option_parse() { --disable-libudev) printf "%s" -Dlibudev=disabled ;; --enable-libusb) printf "%s" -Dlibusb=enabled ;; --disable-libusb) printf "%s" -Dlibusb=disabled ;; - --enable-libxml2) printf "%s" -Dlibxml2=enabled ;; - --disable-libxml2) printf "%s" -Dlibxml2=disabled ;; --enable-linux-aio) printf "%s" -Dlinux_aio=enabled ;; --disable-linux-aio) printf "%s" -Dlinux_aio=disabled ;; --enable-linux-io-uring) printf "%s" -Dlinux_io_uring=enabled ;; From dabee8381a2c3b838f9398bb9f05a012df8768c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 4 Feb 2022 20:43:18 +0000 Subject: [PATCH 379/460] tests/lcitool: Refresh submodule and remove libxml2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous commit removed all uses of libxml2. Refresh lcitool submodule, update qemu.yml and refresh the generated files by running: $ make lcitool-refresh Note: This refreshment also removes libudev dependency on Fedora and CentOS due to libvirt-ci commit 18bfaee ("mappings: Improve mapping for libudev"), since "The udev project has been absorbed by the systemd project", and lttng-ust on FreeBSD runners due to libvirt-ci commit 6dd9b6f ("guests: drop lttng-ust from FreeBSD platform"). Reviewed-by: Daniel P. Berrangé Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Alex Bennée Message-Id: <20220121154134.315047-6-f4bug@amsat.org> Message-Id: <20220204204335.1689602-10-alex.bennee@linaro.org> --- .gitlab-ci.d/cirrus/freebsd-12.vars | 2 +- .gitlab-ci.d/cirrus/freebsd-13.vars | 2 +- .gitlab-ci.d/cirrus/macos-11.vars | 2 +- tests/docker/dockerfiles/alpine.docker | 4 ++-- tests/docker/dockerfiles/centos8.docker | 4 +--- tests/docker/dockerfiles/fedora.docker | 4 +--- tests/docker/dockerfiles/opensuse-leap.docker | 3 +-- tests/docker/dockerfiles/ubuntu1804.docker | 3 +-- tests/docker/dockerfiles/ubuntu2004.docker | 3 +-- tests/lcitool/libvirt-ci | 2 +- tests/lcitool/projects/qemu.yml | 1 - 11 files changed, 11 insertions(+), 19 deletions(-) diff --git a/.gitlab-ci.d/cirrus/freebsd-12.vars b/.gitlab-ci.d/cirrus/freebsd-12.vars index 9c52266811..07f313aa3a 100644 --- a/.gitlab-ci.d/cirrus/freebsd-12.vars +++ b/.gitlab-ci.d/cirrus/freebsd-12.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 bzip2 ca_root_nss capstone4 ccache cdrkit-genisoimage ctags curl cyrus-sasl dbus diffutils dtc gettext git glib gmake gnutls gsed gtk3 libepoxy libffi libgcrypt libjpeg-turbo libnfs libspice-server libssh libtasn1 libxml2 llvm lttng-ust lzo2 meson ncurses nettle ninja opencv p5-Test-Harness perl5 pixman pkgconf png py38-numpy py38-pillow py38-pip py38-sphinx py38-sphinx_rtd_theme py38-virtualenv py38-yaml python3 rpm2cpio sdl2 sdl2_image snappy spice-protocol tesseract texinfo usbredir virglrenderer vte3 zstd' +PKGS='alsa-lib bash bzip2 ca_root_nss capstone4 ccache cdrkit-genisoimage ctags curl cyrus-sasl dbus diffutils dtc fusefs-libs3 gettext git glib gmake gnutls gsed gtk3 libepoxy libffi libgcrypt libjpeg-turbo libnfs libspice-server libssh libtasn1 llvm lzo2 meson ncurses nettle ninja opencv p5-Test-Harness perl5 pixman pkgconf png py38-numpy py38-pillow py38-pip py38-sphinx py38-sphinx_rtd_theme py38-virtualenv py38-yaml python3 rpm2cpio sdl2 sdl2_image snappy spice-protocol tesseract texinfo usbredir virglrenderer vte3 zstd' PYPI_PKGS='' PYTHON='/usr/local/bin/python3' diff --git a/.gitlab-ci.d/cirrus/freebsd-13.vars b/.gitlab-ci.d/cirrus/freebsd-13.vars index 7b44dba324..8a648dda1e 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 bzip2 ca_root_nss capstone4 ccache cdrkit-genisoimage ctags curl cyrus-sasl dbus diffutils dtc gettext git glib gmake gnutls gsed gtk3 libepoxy libffi libgcrypt libjpeg-turbo libnfs libspice-server libssh libtasn1 libxml2 llvm lttng-ust lzo2 meson ncurses nettle ninja opencv p5-Test-Harness perl5 pixman pkgconf png py38-numpy py38-pillow py38-pip py38-sphinx py38-sphinx_rtd_theme py38-virtualenv py38-yaml python3 rpm2cpio sdl2 sdl2_image snappy spice-protocol tesseract texinfo usbredir virglrenderer vte3 zstd' +PKGS='alsa-lib bash bzip2 ca_root_nss capstone4 ccache cdrkit-genisoimage ctags curl cyrus-sasl dbus diffutils dtc fusefs-libs3 gettext git glib gmake gnutls gsed gtk3 libepoxy libffi libgcrypt libjpeg-turbo libnfs libspice-server libssh libtasn1 llvm lzo2 meson ncurses nettle ninja opencv p5-Test-Harness perl5 pixman pkgconf png py38-numpy py38-pillow py38-pip py38-sphinx py38-sphinx_rtd_theme py38-virtualenv py38-yaml python3 rpm2cpio sdl2 sdl2_image snappy spice-protocol tesseract texinfo usbredir virglrenderer vte3 zstd' PYPI_PKGS='' PYTHON='/usr/local/bin/python3' diff --git a/.gitlab-ci.d/cirrus/macos-11.vars b/.gitlab-ci.d/cirrus/macos-11.vars index 613d1373c2..08183f8793 100644 --- a/.gitlab-ci.d/cirrus/macos-11.vars +++ b/.gitlab-ci.d/cirrus/macos-11.vars @@ -11,6 +11,6 @@ MAKE='/usr/local/bin/gmake' NINJA='/usr/local/bin/ninja' PACKAGING_COMMAND='brew' PIP3='/usr/local/bin/pip3' -PKGS='bash bc bzip2 capstone ccache cpanminus ctags curl dbus diffutils dtc gcovr gettext git glib gnu-sed gnutls gtk+3 jemalloc jpeg-turbo libepoxy libffi libgcrypt libiscsi libnfs libpng libslirp libssh libtasn1 libusb libxml2 llvm lzo make meson ncurses nettle ninja perl pixman pkg-config python3 rpm2cpio sdl2 sdl2_image snappy sparse spice-protocol tesseract texinfo usbredir vde vte3 zlib zstd' +PKGS='bash bc bzip2 capstone ccache cpanminus ctags curl dbus diffutils dtc gcovr gettext git glib gnu-sed gnutls gtk+3 jemalloc jpeg-turbo libepoxy libffi libgcrypt libiscsi libnfs libpng libslirp libssh libtasn1 libusb llvm lzo make meson ncurses nettle ninja perl pixman pkg-config python3 rpm2cpio sdl2 sdl2_image snappy sparse spice-protocol tesseract texinfo usbredir vde vte3 zlib zstd' PYPI_PKGS='PyYAML numpy pillow sphinx sphinx-rtd-theme virtualenv' PYTHON='/usr/local/bin/python3' diff --git a/tests/docker/dockerfiles/alpine.docker b/tests/docker/dockerfiles/alpine.docker index eb2251c81c..591af43d60 100644 --- a/tests/docker/dockerfiles/alpine.docker +++ b/tests/docker/dockerfiles/alpine.docker @@ -1,6 +1,6 @@ # THIS FILE WAS AUTO-GENERATED # -# $ lcitool dockerfile alpine-edge qemu +# $ lcitool dockerfile --layers all alpine-edge qemu # # https://gitlab.com/libvirt/libvirt-ci @@ -56,7 +56,6 @@ RUN apk update && \ libtasn1-dev \ liburing-dev \ libusb-dev \ - libxml2-dev \ linux-pam-dev \ llvm11 \ lttng-ust-dev \ @@ -109,6 +108,7 @@ RUN apk update && \ zlib-dev \ zlib-static \ zstd-dev && \ + apk list | 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 cbb909d02b..3abac7a8b1 100644 --- a/tests/docker/dockerfiles/centos8.docker +++ b/tests/docker/dockerfiles/centos8.docker @@ -1,6 +1,6 @@ # THIS FILE WAS AUTO-GENERATED # -# $ lcitool dockerfile centos-8 qemu +# $ lcitool dockerfile --layers all centos-8 qemu # # https://gitlab.com/libvirt/libvirt-ci @@ -69,10 +69,8 @@ RUN dnf update -y && \ libssh-devel \ libtasn1-devel \ libubsan \ - libudev-devel \ liburing-devel \ libusbx-devel \ - libxml2-devel \ libzstd-devel \ llvm \ lttng-ust-devel \ diff --git a/tests/docker/dockerfiles/fedora.docker b/tests/docker/dockerfiles/fedora.docker index 60207f3da3..1d01cd9440 100644 --- a/tests/docker/dockerfiles/fedora.docker +++ b/tests/docker/dockerfiles/fedora.docker @@ -1,6 +1,6 @@ # THIS FILE WAS AUTO-GENERATED # -# $ lcitool dockerfile fedora-35 qemu +# $ lcitool dockerfile --layers all fedora-35 qemu # # https://gitlab.com/libvirt/libvirt-ci @@ -77,10 +77,8 @@ exec "$@"' > /usr/bin/nosync && \ libssh-devel \ libtasn1-devel \ libubsan \ - libudev-devel \ liburing-devel \ libusbx-devel \ - libxml2-devel \ libzstd-devel \ llvm \ lttng-ust-devel \ diff --git a/tests/docker/dockerfiles/opensuse-leap.docker b/tests/docker/dockerfiles/opensuse-leap.docker index f57d8cfb29..1b78d8369a 100644 --- a/tests/docker/dockerfiles/opensuse-leap.docker +++ b/tests/docker/dockerfiles/opensuse-leap.docker @@ -1,6 +1,6 @@ # THIS FILE WAS AUTO-GENERATED # -# $ lcitool dockerfile opensuse-leap-152 qemu +# $ lcitool dockerfile --layers all opensuse-leap-152 qemu # # https://gitlab.com/libvirt/libvirt-ci @@ -71,7 +71,6 @@ RUN zypper update -y && \ libudev-devel \ liburing-devel \ libusb-1_0-devel \ - libxml2-devel \ libzstd-devel \ llvm \ lttng-ust-devel \ diff --git a/tests/docker/dockerfiles/ubuntu1804.docker b/tests/docker/dockerfiles/ubuntu1804.docker index 0ffa3c4d4b..003ee2cfed 100644 --- a/tests/docker/dockerfiles/ubuntu1804.docker +++ b/tests/docker/dockerfiles/ubuntu1804.docker @@ -1,6 +1,6 @@ # THIS FILE WAS AUTO-GENERATED # -# $ lcitool dockerfile ubuntu-1804 qemu +# $ lcitool dockerfile --layers all ubuntu-1804 qemu # # https://gitlab.com/libvirt/libvirt-ci @@ -89,7 +89,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libvirglrenderer-dev \ libvte-2.91-dev \ libxen-dev \ - libxml2-dev \ libzstd-dev \ llvm \ locales \ diff --git a/tests/docker/dockerfiles/ubuntu2004.docker b/tests/docker/dockerfiles/ubuntu2004.docker index 4e562dfdcd..8993d2d9e0 100644 --- a/tests/docker/dockerfiles/ubuntu2004.docker +++ b/tests/docker/dockerfiles/ubuntu2004.docker @@ -1,6 +1,6 @@ # THIS FILE WAS AUTO-GENERATED # -# $ lcitool dockerfile ubuntu-2004 qemu +# $ lcitool dockerfile --layers all ubuntu-2004 qemu # # https://gitlab.com/libvirt/libvirt-ci @@ -91,7 +91,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libvirglrenderer-dev \ libvte-2.91-dev \ libxen-dev \ - libxml2-dev \ libzstd-dev \ llvm \ locales \ diff --git a/tests/lcitool/libvirt-ci b/tests/lcitool/libvirt-ci index 29cec2153b..6dd9b6fab1 160000 --- a/tests/lcitool/libvirt-ci +++ b/tests/lcitool/libvirt-ci @@ -1 +1 @@ -Subproject commit 29cec2153b9a4dbb2e66f1cbc9866a4eff519cfd +Subproject commit 6dd9b6fab1fe081b16bc975485d7a02c81ba5fbe diff --git a/tests/lcitool/projects/qemu.yml b/tests/lcitool/projects/qemu.yml index ed5ab1407a..de51a2f1dd 100644 --- a/tests/lcitool/projects/qemu.yml +++ b/tests/lcitool/projects/qemu.yml @@ -63,7 +63,6 @@ packages: - liburing - libusbx - libvdeplug - - libxml2 - libzstd - llvm - lttng-ust From d2b7bb099fb1f8827f9b5e648f5b6ddd01191719 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 4 Feb 2022 20:43:19 +0000 Subject: [PATCH 380/460] tests: Manually remove libxml2 on MSYS2 runners MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit lcitool doesn't support MSYS2 targets, so manually remove this now unnecessary library. Reviewed-by: Daniel P. Berrangé Reviewed-by: Thomas Huth Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Alex Bennée Message-Id: <20220121154134.315047-7-f4bug@amsat.org> Message-Id: <20220204204335.1689602-11-alex.bennee@linaro.org> --- .cirrus.yml | 1 - .gitlab-ci.d/windows.yml | 2 -- 2 files changed, 3 deletions(-) diff --git a/.cirrus.yml b/.cirrus.yml index 02c43a074a..7552d70974 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -32,7 +32,6 @@ windows_msys2_task: mingw-w64-x86_64-libgcrypt mingw-w64-x86_64-libpng mingw-w64-x86_64-libssh - mingw-w64-x86_64-libxml2 mingw-w64-x86_64-snappy mingw-w64-x86_64-libusb mingw-w64-x86_64-usbredir diff --git a/.gitlab-ci.d/windows.yml b/.gitlab-ci.d/windows.yml index 62dd9ed832..1df1630349 100644 --- a/.gitlab-ci.d/windows.yml +++ b/.gitlab-ci.d/windows.yml @@ -44,7 +44,6 @@ msys2-64bit: mingw-w64-x86_64-libssh mingw-w64-x86_64-libtasn1 mingw-w64-x86_64-libusb - mingw-w64-x86_64-libxml2 mingw-w64-x86_64-nettle mingw-w64-x86_64-ninja mingw-w64-x86_64-pixman @@ -80,7 +79,6 @@ msys2-32bit: mingw-w64-i686-libssh mingw-w64-i686-libtasn1 mingw-w64-i686-libusb - mingw-w64-i686-libxml2 mingw-w64-i686-lzo2 mingw-w64-i686-ninja mingw-w64-i686-pixman From 4491c46879a10c256f73a767822039bd3ec23c76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 4 Feb 2022 20:43:20 +0000 Subject: [PATCH 381/460] tests/lcitool: Install libibumad to cover RDMA on Debian based distros MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On Debian we also need libibumad to enable RDMA: $ ../configure --enable-rdma ERROR: OpenFabrics librdmacm/libibverbs/libibumad not present. Your options: (1) Fast: Install infiniband packages (devel) from your distro. (2) Cleanest: Install libraries from www.openfabrics.org (3) Also: Install softiwarp if you don't have RDMA hardware Add the dependency to lcitool's qemu.yml (where librdmacm and libibverbs are already listed) and refresh the generated files by running: $ make lcitool-refresh Reviewed-by: Daniel P. Berrangé Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Alex Bennée Message-Id: <20220121154134.315047-8-f4bug@amsat.org> Message-Id: <20220204204335.1689602-12-alex.bennee@linaro.org> --- tests/docker/dockerfiles/ubuntu1804.docker | 1 + tests/docker/dockerfiles/ubuntu2004.docker | 1 + tests/lcitool/projects/qemu.yml | 1 + 3 files changed, 3 insertions(+) diff --git a/tests/docker/dockerfiles/ubuntu1804.docker b/tests/docker/dockerfiles/ubuntu1804.docker index 003ee2cfed..699f2dfc6a 100644 --- a/tests/docker/dockerfiles/ubuntu1804.docker +++ b/tests/docker/dockerfiles/ubuntu1804.docker @@ -52,6 +52,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libglib2.0-dev \ libgnutls28-dev \ libgtk-3-dev \ + libibumad-dev \ libibverbs-dev \ libiscsi-dev \ libjemalloc-dev \ diff --git a/tests/docker/dockerfiles/ubuntu2004.docker b/tests/docker/dockerfiles/ubuntu2004.docker index 8993d2d9e0..87513125b8 100644 --- a/tests/docker/dockerfiles/ubuntu2004.docker +++ b/tests/docker/dockerfiles/ubuntu2004.docker @@ -53,6 +53,7 @@ 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 de51a2f1dd..958868a6ee 100644 --- a/tests/lcitool/projects/qemu.yml +++ b/tests/lcitool/projects/qemu.yml @@ -43,6 +43,7 @@ packages: - libfdt - libffi - libgcrypt + - libibumad - libibverbs - libiscsi - libjemalloc From bda8bebad0c073e69ef16eb3efb44aa4306a69ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 4 Feb 2022 20:43:21 +0000 Subject: [PATCH 382/460] docs/devel: mention our .editorconfig MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ideally we should keep all our automatic formatting gubins in here. Signed-off-by: Alex Bennée Reviewed-by: Richard Henderson Tested-by: Philippe Mathieu-Daudé Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20220204204335.1689602-13-alex.bennee@linaro.org> --- docs/devel/style.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/devel/style.rst b/docs/devel/style.rst index 793a8d4280..9e66d133e1 100644 --- a/docs/devel/style.rst +++ b/docs/devel/style.rst @@ -12,6 +12,10 @@ patches before submitting. Formatting and style ******************** +The repository includes a ``.editorconfig`` file which can help with +getting the right settings for your preferred $EDITOR. See +``_ for details. + Whitespace ========== From 6340af7a949589cf07b5194edbbfb352734d8a3b Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Fri, 4 Feb 2022 20:43:22 +0000 Subject: [PATCH 383/460] gitlab: fall back to commit hash in qemu-setup filename MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Personal repos may not have release tags (v6.0.0, v6.1.0, etc) and this causes cross_system_build_job to fail when pretty-printing a unique qemu-setup-*.exe name: version="$(git describe --match v[0-9]*)"; ^^^^^^^^^^ fails ^^^^^^^^^^^ mv -v qemu-setup*.exe qemu-setup-${version}.exe; Fall back to the short commit hash if necessary. This fixes CI failures that Greg Kurz and I experienced in our personal repos. Cc: Greg Kurz Cc: Gerd Hoffmann Cc: Philippe Mathieu-Daudé Signed-off-by: Stefan Hajnoczi Reviewed-by: Greg Kurz Reviewed-by: Thomas Huth Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20220125173454.10381-1-stefanha@redhat.com> Signed-off-by: Alex Bennée Message-Id: <20220204204335.1689602-14-alex.bennee@linaro.org> --- .gitlab-ci.d/crossbuild-template.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.d/crossbuild-template.yml b/.gitlab-ci.d/crossbuild-template.yml index 10d22dcf6c..29c3c2b826 100644 --- a/.gitlab-ci.d/crossbuild-template.yml +++ b/.gitlab-ci.d/crossbuild-template.yml @@ -14,7 +14,7 @@ - make -j$(expr $(nproc) + 1) all check-build $MAKE_CHECK_ARGS - if grep -q "EXESUF=.exe" config-host.mak; then make installer; - version="$(git describe --match v[0-9]*)"; + version="$(git describe --match v[0-9]* 2>/dev/null || git rev-parse --short HEAD)"; mv -v qemu-setup*.exe qemu-setup-${version}.exe; fi From ab4f987c4c56a55977922595f95236890db88edf Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 4 Feb 2022 20:43:23 +0000 Subject: [PATCH 384/460] tests/lcitool: Allow lcitool-refresh in out-of-tree builds, too MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When running "make lcitool-refresh" in an out-of-tree build, it currently fails with an error message from git like this: fatal: not a git repository (or any parent up to mount point /) Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set). Fix it by changing to the source directory first before updating the submodule. Signed-off-by: Thomas Huth Reviewed-by: Daniel P. Berrangé Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20220201085554.85733-1-thuth@redhat.com> Signed-off-by: Alex Bennée Message-Id: <20220204204335.1689602-15-alex.bennee@linaro.org> --- tests/lcitool/Makefile.include | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/lcitool/Makefile.include b/tests/lcitool/Makefile.include index cff7c0b814..6b215adcd1 100644 --- a/tests/lcitool/Makefile.include +++ b/tests/lcitool/Makefile.include @@ -13,5 +13,5 @@ lcitool: lcitool-help: lcitool lcitool-refresh: - $(call quiet-command, git submodule update --init $(SRC_PATH)/tests/lcitool/libvirt-ci) + $(call quiet-command, cd $(SRC_PATH) && git submodule update --init tests/lcitool/libvirt-ci) $(call quiet-command, $(LCITOOL_REFRESH)) From 8b11f4c37a1691bbca36dc2756b02acf4ec5977e Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 4 Feb 2022 20:43:24 +0000 Subject: [PATCH 385/460] tests: Update CentOS 8 container to CentOS Stream 8 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Support for CentOS 8 has stopped at the end of 2021, so let's switch to the Stream variant instead. Signed-off-by: Thomas Huth Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Message-Id: <20220201101911.97900-1-thuth@redhat.com> Signed-off-by: Alex Bennée Message-Id: <20220204204335.1689602-16-alex.bennee@linaro.org> --- tests/docker/dockerfiles/centos8.docker | 4 ++-- tests/lcitool/refresh | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/docker/dockerfiles/centos8.docker b/tests/docker/dockerfiles/centos8.docker index 3abac7a8b1..3ede55d09b 100644 --- a/tests/docker/dockerfiles/centos8.docker +++ b/tests/docker/dockerfiles/centos8.docker @@ -1,10 +1,10 @@ # THIS FILE WAS AUTO-GENERATED # -# $ lcitool dockerfile --layers all centos-8 qemu +# $ lcitool dockerfile --layers all centos-stream-8 qemu # # https://gitlab.com/libvirt/libvirt-ci -FROM docker.io/library/centos:8 +FROM quay.io/centos/centos:stream8 RUN dnf update -y && \ dnf install 'dnf-command(config-manager)' -y && \ diff --git a/tests/lcitool/refresh b/tests/lcitool/refresh index 25301f2ef0..4ab90a310a 100755 --- a/tests/lcitool/refresh +++ b/tests/lcitool/refresh @@ -77,7 +77,7 @@ ubuntu2004_tsanhack = [ ] try: - generate_dockerfile("centos8", "centos-8") + generate_dockerfile("centos8", "centos-stream-8") generate_dockerfile("fedora", "fedora-35") generate_dockerfile("ubuntu1804", "ubuntu-1804", trailer="".join(ubuntu1804_skipssh)) From 3bdc19af00937191623fb0a2cacbc4ad54882072 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 4 Feb 2022 20:43:25 +0000 Subject: [PATCH 386/460] tests/tcg/sh4: disable another unreliable test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Given the other failures it looks like general thread handling on sh4 is sketchy. It fails more often on CI than on my developer machine though. See https://gitlab.com/qemu-project/qemu/-/issues/856 for more details. Signed-off-by: Alex Bennée Cc: Yoshinori Sato Cc: Laurent Vivier Tested-by: Philippe Mathieu-Daudé Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20220204204335.1689602-17-alex.bennee@linaro.org> --- tests/tcg/sh4/Makefile.target | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/tcg/sh4/Makefile.target b/tests/tcg/sh4/Makefile.target index 0e96aeff16..620ccc23c1 100644 --- a/tests/tcg/sh4/Makefile.target +++ b/tests/tcg/sh4/Makefile.target @@ -18,3 +18,7 @@ run-linux-test: linux-test $(call skip-test, $<, "BROKEN") run-plugin-linux-test-with-%: $(call skip-test, $<, "BROKEN") + +# This test is currently unreliable: https://gitlab.com/qemu-project/qemu/-/issues/856 +run-plugin-threadcount-with-%: + $(call skip-test, $<, "BROKEN") From d9a6bad542cd796c785ff19793fa7ae24da6a97d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 4 Feb 2022 20:43:26 +0000 Subject: [PATCH 387/460] docs: remove references to TCG tracing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Users wanting this sort of functionality should turn to TCG plugins instead. Signed-off-by: Alex Bennée Reviewed-by: Richard Henderson Cc: Luis Vilanova Cc: Stefan Hajnoczi Message-Id: <20220204204335.1689602-18-alex.bennee@linaro.org> --- docs/about/removed-features.rst | 13 +++++ docs/devel/tracing.rst | 85 --------------------------------- 2 files changed, 13 insertions(+), 85 deletions(-) diff --git a/docs/about/removed-features.rst b/docs/about/removed-features.rst index 4c4da20d0f..b0156e0f25 100644 --- a/docs/about/removed-features.rst +++ b/docs/about/removed-features.rst @@ -601,6 +601,19 @@ the upstream Linux kernel in 2018, and it has also been dropped from glibc, so there is no new Linux development taking place with this architecture. For running the old binaries, you can use older versions of QEMU. +TCG introspection features +-------------------------- + +TCG trace-events (since 6.2) +'''''''''''''''''''''''''''' + +The ability to add new TCG trace points had bit rotted and as the +feature can be replicated with TCG plugins it was removed. If +any user is currently using this feature and needs help with +converting to using TCG plugins they should contact the qemu-devel +mailing list. + + System emulator devices ----------------------- diff --git a/docs/devel/tracing.rst b/docs/devel/tracing.rst index 4290ac42ee..ec9a687cfd 100644 --- a/docs/devel/tracing.rst +++ b/docs/devel/tracing.rst @@ -413,88 +413,3 @@ disabled, this check will have no performance impact. return ptr; } -"tcg" ------ - -Guest code generated by TCG can be traced by defining an event with the "tcg" -event property. Internally, this property generates two events: -"_trans" to trace the event at translation time, and -"_exec" to trace the event at execution time. - -Instead of using these two events, you should instead use the function -"trace__tcg" during translation (TCG code generation). This function -will automatically call "trace__trans", and will generate the -necessary TCG code to call "trace__exec" during guest code execution. - -Events with the "tcg" property can be declared in the "trace-events" file with a -mix of native and TCG types, and "trace__tcg" will gracefully forward -them to the "_trans" and "_exec" events. Since TCG values -are not known at translation time, these are ignored by the "_trans" -event. Because of this, the entry in the "trace-events" file needs two printing -formats (separated by a comma):: - - tcg foo(uint8_t a1, TCGv_i32 a2) "a1=%d", "a1=%d a2=%d" - -For example:: - - #include "trace-tcg.h" - - void some_disassembly_func (...) - { - uint8_t a1 = ...; - TCGv_i32 a2 = ...; - trace_foo_tcg(a1, a2); - } - -This will immediately call:: - - void trace_foo_trans(uint8_t a1); - -and will generate the TCG code to call:: - - void trace_foo(uint8_t a1, uint32_t a2); - -"vcpu" ------- - -Identifies events that trace vCPU-specific information. It implicitly adds a -"CPUState*" argument, and extends the tracing print format to show the vCPU -information. If used together with the "tcg" property, it adds a second -"TCGv_env" argument that must point to the per-target global TCG register that -points to the vCPU when guest code is executed (usually the "cpu_env" variable). - -The "tcg" and "vcpu" properties are currently only honored in the root -./trace-events file. - -The following example events:: - - foo(uint32_t a) "a=%x" - vcpu bar(uint32_t a) "a=%x" - tcg vcpu baz(uint32_t a) "a=%x", "a=%x" - -Can be used as:: - - #include "trace-tcg.h" - - CPUArchState *env; - TCGv_ptr cpu_env; - - void some_disassembly_func(...) - { - /* trace emitted at this point */ - trace_foo(0xd1); - /* trace emitted at this point */ - trace_bar(env_cpu(env), 0xd2); - /* trace emitted at this point (env) and when guest code is executed (cpu_env) */ - trace_baz_tcg(env_cpu(env), cpu_env, 0xd3); - } - -If the translating vCPU has address 0xc1 and code is later executed by vCPU -0xc2, this would be an example output:: - - // at guest code translation - foo a=0xd1 - bar cpu=0xc1 a=0xd2 - baz_trans cpu=0xc1 a=0xd3 - // at guest code execution - baz_exec cpu=0xc2 a=0xd3 From c51e51005b74162df7a9c568213acca85e254efa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 4 Feb 2022 20:43:27 +0000 Subject: [PATCH 388/460] tracing: remove TCG memory access tracing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If you really want to trace all memory operations TCG plugins gives you a more flexible interface for doing so. Signed-off-by: Alex Bennée Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Cc: Luis Vilanova Cc: Stefan Hajnoczi Message-Id: <20220204204335.1689602-19-alex.bennee@linaro.org> --- accel/tcg/atomic_common.c.inc | 20 -------------------- accel/tcg/atomic_template.h | 12 ------------ accel/tcg/cputlb.c | 2 -- accel/tcg/user-exec.c | 14 -------------- tcg/tcg-op.c | 5 ----- trace-events | 14 -------------- 6 files changed, 67 deletions(-) diff --git a/accel/tcg/atomic_common.c.inc b/accel/tcg/atomic_common.c.inc index 1df1f243e9..6602d7689f 100644 --- a/accel/tcg/atomic_common.c.inc +++ b/accel/tcg/atomic_common.c.inc @@ -13,14 +13,6 @@ * See the COPYING file in the top-level directory. */ -static void atomic_trace_rmw_pre(CPUArchState *env, target_ulong addr, - MemOpIdx oi) -{ - CPUState *cpu = env_cpu(env); - - trace_guest_rmw_before_exec(cpu, addr, oi); -} - static void atomic_trace_rmw_post(CPUArchState *env, target_ulong addr, MemOpIdx oi) { @@ -28,24 +20,12 @@ static void atomic_trace_rmw_post(CPUArchState *env, target_ulong addr, } #if HAVE_ATOMIC128 -static void atomic_trace_ld_pre(CPUArchState *env, target_ulong addr, - MemOpIdx oi) -{ - trace_guest_ld_before_exec(env_cpu(env), addr, oi); -} - static void atomic_trace_ld_post(CPUArchState *env, target_ulong addr, MemOpIdx oi) { qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, oi, QEMU_PLUGIN_MEM_R); } -static void atomic_trace_st_pre(CPUArchState *env, target_ulong addr, - MemOpIdx oi) -{ - trace_guest_st_before_exec(env_cpu(env), addr, oi); -} - static void atomic_trace_st_post(CPUArchState *env, target_ulong addr, MemOpIdx oi) { diff --git a/accel/tcg/atomic_template.h b/accel/tcg/atomic_template.h index 2d917b6b1f..fc165031e8 100644 --- a/accel/tcg/atomic_template.h +++ b/accel/tcg/atomic_template.h @@ -77,7 +77,6 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr, PAGE_READ | PAGE_WRITE, retaddr); DATA_TYPE ret; - atomic_trace_rmw_pre(env, addr, oi); #if DATA_SIZE == 16 ret = atomic16_cmpxchg(haddr, cmpv, newv); #else @@ -97,7 +96,6 @@ ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr, PAGE_READ, retaddr); DATA_TYPE val; - atomic_trace_ld_pre(env, addr, oi); val = atomic16_read(haddr); ATOMIC_MMU_CLEANUP; atomic_trace_ld_post(env, addr, oi); @@ -110,7 +108,6 @@ void ATOMIC_NAME(st)(CPUArchState *env, target_ulong addr, ABI_TYPE val, DATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE, PAGE_WRITE, retaddr); - atomic_trace_st_pre(env, addr, oi); atomic16_set(haddr, val); ATOMIC_MMU_CLEANUP; atomic_trace_st_post(env, addr, oi); @@ -124,7 +121,6 @@ ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr, ABI_TYPE val, PAGE_READ | PAGE_WRITE, retaddr); DATA_TYPE ret; - atomic_trace_rmw_pre(env, addr, oi); ret = qatomic_xchg__nocheck(haddr, val); ATOMIC_MMU_CLEANUP; atomic_trace_rmw_post(env, addr, oi); @@ -138,7 +134,6 @@ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \ DATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE, \ PAGE_READ | PAGE_WRITE, retaddr); \ DATA_TYPE ret; \ - atomic_trace_rmw_pre(env, addr, oi); \ ret = qatomic_##X(haddr, val); \ ATOMIC_MMU_CLEANUP; \ atomic_trace_rmw_post(env, addr, oi); \ @@ -171,7 +166,6 @@ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \ XDATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE, \ PAGE_READ | PAGE_WRITE, retaddr); \ XDATA_TYPE cmp, old, new, val = xval; \ - atomic_trace_rmw_pre(env, addr, oi); \ smp_mb(); \ cmp = qatomic_read__nocheck(haddr); \ do { \ @@ -216,7 +210,6 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr, PAGE_READ | PAGE_WRITE, retaddr); DATA_TYPE ret; - atomic_trace_rmw_pre(env, addr, oi); #if DATA_SIZE == 16 ret = atomic16_cmpxchg(haddr, BSWAP(cmpv), BSWAP(newv)); #else @@ -236,7 +229,6 @@ ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr, PAGE_READ, retaddr); DATA_TYPE val; - atomic_trace_ld_pre(env, addr, oi); val = atomic16_read(haddr); ATOMIC_MMU_CLEANUP; atomic_trace_ld_post(env, addr, oi); @@ -249,7 +241,6 @@ void ATOMIC_NAME(st)(CPUArchState *env, target_ulong addr, ABI_TYPE val, DATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE, PAGE_WRITE, retaddr); - atomic_trace_st_pre(env, addr, oi); val = BSWAP(val); atomic16_set(haddr, val); ATOMIC_MMU_CLEANUP; @@ -264,7 +255,6 @@ ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr, ABI_TYPE val, PAGE_READ | PAGE_WRITE, retaddr); ABI_TYPE ret; - atomic_trace_rmw_pre(env, addr, oi); ret = qatomic_xchg__nocheck(haddr, BSWAP(val)); ATOMIC_MMU_CLEANUP; atomic_trace_rmw_post(env, addr, oi); @@ -278,7 +268,6 @@ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \ DATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE, \ PAGE_READ | PAGE_WRITE, retaddr); \ DATA_TYPE ret; \ - atomic_trace_rmw_pre(env, addr, oi); \ ret = qatomic_##X(haddr, BSWAP(val)); \ ATOMIC_MMU_CLEANUP; \ atomic_trace_rmw_post(env, addr, oi); \ @@ -308,7 +297,6 @@ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \ XDATA_TYPE *haddr = atomic_mmu_lookup(env, addr, oi, DATA_SIZE, \ PAGE_READ | PAGE_WRITE, retaddr); \ XDATA_TYPE ldo, ldn, old, new, val = xval; \ - atomic_trace_rmw_pre(env, addr, oi); \ smp_mb(); \ ldn = qatomic_read__nocheck(haddr); \ do { \ diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index 5e0d0eebc3..82adefe574 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -2140,7 +2140,6 @@ static inline uint64_t cpu_load_helper(CPUArchState *env, abi_ptr addr, { uint64_t ret; - trace_guest_ld_before_exec(env_cpu(env), addr, oi); ret = full_load(env, addr, oi, retaddr); qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, oi, QEMU_PLUGIN_MEM_R); return ret; @@ -2487,7 +2486,6 @@ static inline void cpu_store_helper(CPUArchState *env, target_ulong addr, uint64_t val, MemOpIdx oi, uintptr_t ra, FullStoreHelper *full_store) { - trace_guest_st_before_exec(env_cpu(env), addr, oi); full_store(env, addr, val, oi, ra); qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, oi, QEMU_PLUGIN_MEM_W); } diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c index 6f5d4933f0..8edf0bbaa1 100644 --- a/accel/tcg/user-exec.c +++ b/accel/tcg/user-exec.c @@ -250,7 +250,6 @@ uint8_t cpu_ldb_mmu(CPUArchState *env, abi_ptr addr, uint8_t ret; validate_memop(oi, MO_UB); - trace_guest_ld_before_exec(env_cpu(env), addr, oi); haddr = cpu_mmu_lookup(env, addr, oi, ra, MMU_DATA_LOAD); ret = ldub_p(haddr); clear_helper_retaddr(); @@ -265,7 +264,6 @@ uint16_t cpu_ldw_be_mmu(CPUArchState *env, abi_ptr addr, uint16_t ret; validate_memop(oi, MO_BEUW); - trace_guest_ld_before_exec(env_cpu(env), addr, oi); haddr = cpu_mmu_lookup(env, addr, oi, ra, MMU_DATA_LOAD); ret = lduw_be_p(haddr); clear_helper_retaddr(); @@ -280,7 +278,6 @@ uint32_t cpu_ldl_be_mmu(CPUArchState *env, abi_ptr addr, uint32_t ret; validate_memop(oi, MO_BEUL); - trace_guest_ld_before_exec(env_cpu(env), addr, oi); haddr = cpu_mmu_lookup(env, addr, oi, ra, MMU_DATA_LOAD); ret = ldl_be_p(haddr); clear_helper_retaddr(); @@ -295,7 +292,6 @@ uint64_t cpu_ldq_be_mmu(CPUArchState *env, abi_ptr addr, uint64_t ret; validate_memop(oi, MO_BEUQ); - trace_guest_ld_before_exec(env_cpu(env), addr, oi); haddr = cpu_mmu_lookup(env, addr, oi, ra, MMU_DATA_LOAD); ret = ldq_be_p(haddr); clear_helper_retaddr(); @@ -310,7 +306,6 @@ uint16_t cpu_ldw_le_mmu(CPUArchState *env, abi_ptr addr, uint16_t ret; validate_memop(oi, MO_LEUW); - trace_guest_ld_before_exec(env_cpu(env), addr, oi); haddr = cpu_mmu_lookup(env, addr, oi, ra, MMU_DATA_LOAD); ret = lduw_le_p(haddr); clear_helper_retaddr(); @@ -325,7 +320,6 @@ uint32_t cpu_ldl_le_mmu(CPUArchState *env, abi_ptr addr, uint32_t ret; validate_memop(oi, MO_LEUL); - trace_guest_ld_before_exec(env_cpu(env), addr, oi); haddr = cpu_mmu_lookup(env, addr, oi, ra, MMU_DATA_LOAD); ret = ldl_le_p(haddr); clear_helper_retaddr(); @@ -340,7 +334,6 @@ uint64_t cpu_ldq_le_mmu(CPUArchState *env, abi_ptr addr, uint64_t ret; validate_memop(oi, MO_LEUQ); - trace_guest_ld_before_exec(env_cpu(env), addr, oi); haddr = cpu_mmu_lookup(env, addr, oi, ra, MMU_DATA_LOAD); ret = ldq_le_p(haddr); clear_helper_retaddr(); @@ -354,7 +347,6 @@ void cpu_stb_mmu(CPUArchState *env, abi_ptr addr, uint8_t val, void *haddr; validate_memop(oi, MO_UB); - trace_guest_st_before_exec(env_cpu(env), addr, oi); haddr = cpu_mmu_lookup(env, addr, oi, ra, MMU_DATA_STORE); stb_p(haddr, val); clear_helper_retaddr(); @@ -367,7 +359,6 @@ void cpu_stw_be_mmu(CPUArchState *env, abi_ptr addr, uint16_t val, void *haddr; validate_memop(oi, MO_BEUW); - trace_guest_st_before_exec(env_cpu(env), addr, oi); haddr = cpu_mmu_lookup(env, addr, oi, ra, MMU_DATA_STORE); stw_be_p(haddr, val); clear_helper_retaddr(); @@ -380,7 +371,6 @@ void cpu_stl_be_mmu(CPUArchState *env, abi_ptr addr, uint32_t val, void *haddr; validate_memop(oi, MO_BEUL); - trace_guest_st_before_exec(env_cpu(env), addr, oi); haddr = cpu_mmu_lookup(env, addr, oi, ra, MMU_DATA_STORE); stl_be_p(haddr, val); clear_helper_retaddr(); @@ -393,7 +383,6 @@ void cpu_stq_be_mmu(CPUArchState *env, abi_ptr addr, uint64_t val, void *haddr; validate_memop(oi, MO_BEUQ); - trace_guest_st_before_exec(env_cpu(env), addr, oi); haddr = cpu_mmu_lookup(env, addr, oi, ra, MMU_DATA_STORE); stq_be_p(haddr, val); clear_helper_retaddr(); @@ -406,7 +395,6 @@ void cpu_stw_le_mmu(CPUArchState *env, abi_ptr addr, uint16_t val, void *haddr; validate_memop(oi, MO_LEUW); - trace_guest_st_before_exec(env_cpu(env), addr, oi); haddr = cpu_mmu_lookup(env, addr, oi, ra, MMU_DATA_STORE); stw_le_p(haddr, val); clear_helper_retaddr(); @@ -419,7 +407,6 @@ void cpu_stl_le_mmu(CPUArchState *env, abi_ptr addr, uint32_t val, void *haddr; validate_memop(oi, MO_LEUL); - trace_guest_st_before_exec(env_cpu(env), addr, oi); haddr = cpu_mmu_lookup(env, addr, oi, ra, MMU_DATA_STORE); stl_le_p(haddr, val); clear_helper_retaddr(); @@ -432,7 +419,6 @@ void cpu_stq_le_mmu(CPUArchState *env, abi_ptr addr, uint64_t val, void *haddr; validate_memop(oi, MO_LEUQ); - trace_guest_st_before_exec(env_cpu(env), addr, oi); haddr = cpu_mmu_lookup(env, addr, oi, ra, MMU_DATA_STORE); stq_le_p(haddr, val); clear_helper_retaddr(); diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index 61b492d89f..65e1c94c2d 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -27,7 +27,6 @@ #include "tcg/tcg.h" #include "tcg/tcg-op.h" #include "tcg/tcg-mo.h" -#include "trace-tcg.h" #include "exec/plugin-gen.h" /* Reduce the number of ifdefs below. This assumes that all uses of @@ -2877,7 +2876,6 @@ void tcg_gen_qemu_ld_i32(TCGv_i32 val, TCGv addr, TCGArg idx, MemOp memop) tcg_gen_req_mo(TCG_MO_LD_LD | TCG_MO_ST_LD); memop = tcg_canonicalize_memop(memop, 0, 0); oi = make_memop_idx(memop, idx); - trace_guest_ld_before_tcg(tcg_ctx->cpu, cpu_env, addr, oi); orig_memop = memop; if (!TCG_TARGET_HAS_MEMORY_BSWAP && (memop & MO_BSWAP)) { @@ -2916,7 +2914,6 @@ void tcg_gen_qemu_st_i32(TCGv_i32 val, TCGv addr, TCGArg idx, MemOp memop) tcg_gen_req_mo(TCG_MO_LD_ST | TCG_MO_ST_ST); memop = tcg_canonicalize_memop(memop, 0, 1); oi = make_memop_idx(memop, idx); - trace_guest_st_before_tcg(tcg_ctx->cpu, cpu_env, addr, oi); if (!TCG_TARGET_HAS_MEMORY_BSWAP && (memop & MO_BSWAP)) { swap = tcg_temp_new_i32(); @@ -2965,7 +2962,6 @@ void tcg_gen_qemu_ld_i64(TCGv_i64 val, TCGv addr, TCGArg idx, MemOp memop) tcg_gen_req_mo(TCG_MO_LD_LD | TCG_MO_ST_LD); memop = tcg_canonicalize_memop(memop, 1, 0); oi = make_memop_idx(memop, idx); - trace_guest_ld_before_tcg(tcg_ctx->cpu, cpu_env, addr, oi); orig_memop = memop; if (!TCG_TARGET_HAS_MEMORY_BSWAP && (memop & MO_BSWAP)) { @@ -3013,7 +3009,6 @@ void tcg_gen_qemu_st_i64(TCGv_i64 val, TCGv addr, TCGArg idx, MemOp memop) tcg_gen_req_mo(TCG_MO_LD_ST | TCG_MO_ST_ST); memop = tcg_canonicalize_memop(memop, 1, 1); oi = make_memop_idx(memop, idx); - trace_guest_st_before_tcg(tcg_ctx->cpu, cpu_env, addr, oi); if (!TCG_TARGET_HAS_MEMORY_BSWAP && (memop & MO_BSWAP)) { swap = tcg_temp_new_i64(); diff --git a/trace-events b/trace-events index a637a61eba..bc71006675 100644 --- a/trace-events +++ b/trace-events @@ -117,20 +117,6 @@ vcpu guest_cpu_exit(void) # Targets: all vcpu guest_cpu_reset(void) -# tcg/tcg-op.c - -# @vaddr: Access' virtual address. -# @memopidx: Access' information (see below). -# -# Start virtual memory access (before any potential access violation). -# Does not include memory accesses performed by devices. -# -# Mode: user, softmmu -# Targets: TCG(all) -vcpu tcg guest_ld_before(TCGv vaddr, uint32_t memopidx) "info=%d", "vaddr=0x%016"PRIx64" memopidx=0x%x" -vcpu tcg guest_st_before(TCGv vaddr, uint32_t memopidx) "info=%d", "vaddr=0x%016"PRIx64" memopidx=0x%x" -vcpu tcg guest_rmw_before(TCGv vaddr, uint32_t memopidx) "info=%d", "vaddr=0x%016"PRIx64" memopidx=0x%x" - # include/user/syscall-trace.h # @num: System call number. From d201cf7a7323521a2abe4771298579673371a3cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 4 Feb 2022 20:43:28 +0000 Subject: [PATCH 389/460] tracing: remove the trace-tcg includes from the build MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Alex Bennée Reviewed-by: Richard Henderson Cc: Luis Vilanova Cc: Stefan Hajnoczi Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20220204204335.1689602-20-alex.bennee@linaro.org> --- include/exec/helper-proto.h | 1 - include/exec/helper-tcg.h | 1 - include/trace-tcg.h | 6 ------ trace/meson.build | 1 - 4 files changed, 9 deletions(-) delete mode 100644 include/trace-tcg.h diff --git a/include/exec/helper-proto.h b/include/exec/helper-proto.h index ba100793a7..c4b1bda632 100644 --- a/include/exec/helper-proto.h +++ b/include/exec/helper-proto.h @@ -38,7 +38,6 @@ dh_ctype(ret) HELPER(name) (dh_ctype(t1), dh_ctype(t2), dh_ctype(t3), \ #define IN_HELPER_PROTO #include "helper.h" -#include "trace/generated-helpers.h" #include "accel/tcg/tcg-runtime.h" #include "accel/tcg/plugin-helpers.h" diff --git a/include/exec/helper-tcg.h b/include/exec/helper-tcg.h index 16cd318b83..3933258f1a 100644 --- a/include/exec/helper-tcg.h +++ b/include/exec/helper-tcg.h @@ -59,7 +59,6 @@ | dh_typemask(t5, 5) | dh_typemask(t6, 6) | dh_typemask(t7, 7) }, #include "helper.h" -#include "trace/generated-helpers.h" #include "accel/tcg/tcg-runtime.h" #include "accel/tcg/plugin-helpers.h" diff --git a/include/trace-tcg.h b/include/trace-tcg.h deleted file mode 100644 index da68608c85..0000000000 --- a/include/trace-tcg.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef TRACE_TCG_H -#define TRACE_TCG_H - -#include "trace/generated-tcg-tracers.h" - -#endif /* TRACE_TCG_H */ diff --git a/trace/meson.build b/trace/meson.build index c4794a1f2a..29f3bffd5a 100644 --- a/trace/meson.build +++ b/trace/meson.build @@ -67,7 +67,6 @@ trace_events_all = custom_target('trace-events-all', install_dir: qemu_datadir) foreach d : [ - ['generated-tcg-tracers.h', 'tcg-h'], ['generated-helpers.c', 'tcg-helper-c'], ['generated-helpers.h', 'tcg-helper-h'], ['generated-helpers-wrappers.h', 'tcg-helper-wrapper-h'], From 126d4123c50a78a99e04196126d42627911ef5b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 4 Feb 2022 20:43:29 +0000 Subject: [PATCH 390/460] tracing: excise the tcg related from tracetool MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now we have no TCG trace events and no longer handle them in the code we can remove the handling from the tracetool to generate them. vcpu tracing is still available although the existing syscall event is an exercise in redundancy (plugins and -strace can also get the information). Signed-off-by: Alex Bennée Reviewed-by: Richard Henderson Cc: Luis Vilanova Cc: Stefan Hajnoczi Message-Id: <20220204204335.1689602-21-alex.bennee@linaro.org> --- include/exec/helper-gen.h | 2 - meson.build | 4 - scripts/tracetool/__init__.py | 41 +-------- scripts/tracetool/format/tcg_h.py | 83 ------------------- scripts/tracetool/format/tcg_helper_c.py | 79 ------------------ scripts/tracetool/format/tcg_helper_h.py | 48 ----------- .../tracetool/format/tcg_helper_wrapper_h.py | 70 ---------------- scripts/tracetool/vcpu.py | 14 +--- trace/meson.build | 13 --- 9 files changed, 4 insertions(+), 350 deletions(-) delete mode 100644 scripts/tracetool/format/tcg_h.py delete mode 100644 scripts/tracetool/format/tcg_helper_c.py delete mode 100644 scripts/tracetool/format/tcg_helper_h.py delete mode 100644 scripts/tracetool/format/tcg_helper_wrapper_h.py diff --git a/include/exec/helper-gen.h b/include/exec/helper-gen.h index 1c2e7a8ed3..7b6ca975ef 100644 --- a/include/exec/helper-gen.h +++ b/include/exec/helper-gen.h @@ -79,8 +79,6 @@ static inline void glue(gen_helper_, name)(dh_retvar_decl(ret) \ } #include "helper.h" -#include "trace/generated-helpers.h" -#include "trace/generated-helpers-wrappers.h" #include "accel/tcg/tcg-runtime.h" #include "accel/tcg/plugin-helpers.h" diff --git a/meson.build b/meson.build index 82db1e7e74..ae5f7eec6e 100644 --- a/meson.build +++ b/meson.build @@ -2357,19 +2357,15 @@ tracetool_depends = files( 'scripts/tracetool/backend/simple.py', 'scripts/tracetool/backend/syslog.py', 'scripts/tracetool/backend/ust.py', - 'scripts/tracetool/format/tcg_h.py', 'scripts/tracetool/format/ust_events_c.py', 'scripts/tracetool/format/ust_events_h.py', 'scripts/tracetool/format/__init__.py', 'scripts/tracetool/format/d.py', - 'scripts/tracetool/format/tcg_helper_c.py', 'scripts/tracetool/format/simpletrace_stap.py', 'scripts/tracetool/format/c.py', 'scripts/tracetool/format/h.py', - 'scripts/tracetool/format/tcg_helper_h.py', 'scripts/tracetool/format/log_stap.py', 'scripts/tracetool/format/stap.py', - 'scripts/tracetool/format/tcg_helper_wrapper_h.py', 'scripts/tracetool/__init__.py', 'scripts/tracetool/transform.py', 'scripts/tracetool/vcpu.py' diff --git a/scripts/tracetool/__init__.py b/scripts/tracetool/__init__.py index 5bc94d95cf..5393c7fc5c 100644 --- a/scripts/tracetool/__init__.py +++ b/scripts/tracetool/__init__.py @@ -87,8 +87,6 @@ ALLOWED_TYPES = [ "ssize_t", "uintptr_t", "ptrdiff_t", - # Magic substitution is done by tracetool - "TCGv", ] def validate_type(name): @@ -232,7 +230,7 @@ class Event(object): "(?:(?:(?P\".+),)?\s*(?P\".+))?" "\s*") - _VALID_PROPS = set(["disable", "tcg", "tcg-trans", "tcg-exec", "vcpu"]) + _VALID_PROPS = set(["disable", "vcpu"]) def __init__(self, name, props, fmt, args, lineno, filename, orig=None, event_trans=None, event_exec=None): @@ -321,15 +319,6 @@ class Event(object): fmt = [fmt_trans, fmt] args = Arguments.build(groups["args"]) - if "tcg-trans" in props: - raise ValueError("Invalid property 'tcg-trans'") - if "tcg-exec" in props: - raise ValueError("Invalid property 'tcg-exec'") - if "tcg" not in props and not isinstance(fmt, str): - raise ValueError("Only events with 'tcg' property can have two format strings") - if "tcg" in props and isinstance(fmt, str): - raise ValueError("Events with 'tcg' property must have two format strings") - event = Event(name, props, fmt, args, lineno, filename) # add implicit arguments when using the 'vcpu' property @@ -409,33 +398,7 @@ def read_events(fobj, fname): e.args = (arg0,) + e.args[1:] raise - # transform TCG-enabled events - if "tcg" not in event.properties: - events.append(event) - else: - event_trans = event.copy() - event_trans.name += "_trans" - event_trans.properties += ["tcg-trans"] - event_trans.fmt = event.fmt[0] - # ignore TCG arguments - args_trans = [] - for atrans, aorig in zip( - event_trans.transform(tracetool.transform.TCG_2_HOST).args, - event.args): - if atrans == aorig: - args_trans.append(atrans) - event_trans.args = Arguments(args_trans) - - event_exec = event.copy() - event_exec.name += "_exec" - event_exec.properties += ["tcg-exec"] - event_exec.fmt = event.fmt[1] - event_exec.args = event_exec.args.transform(tracetool.transform.TCG_2_HOST) - - new_event = [event_trans, event_exec] - event.event_trans, event.event_exec = new_event - - events.extend(new_event) + events.append(event) return events diff --git a/scripts/tracetool/format/tcg_h.py b/scripts/tracetool/format/tcg_h.py deleted file mode 100644 index 4d84440aff..0000000000 --- a/scripts/tracetool/format/tcg_h.py +++ /dev/null @@ -1,83 +0,0 @@ -# -*- coding: utf-8 -*- - -""" -Generate .h file for TCG code generation. -""" - -__author__ = "Lluís Vilanova " -__copyright__ = "Copyright 2012-2017, Lluís Vilanova " -__license__ = "GPL version 2 or (at your option) any later version" - -__maintainer__ = "Stefan Hajnoczi" -__email__ = "stefanha@redhat.com" - - -from tracetool import out, Arguments -import tracetool.vcpu - - -def vcpu_transform_args(args): - assert len(args) == 1 - return Arguments([ - args, - # NOTE: this name must be kept in sync with the one in "tcg_h" - # NOTE: Current helper code uses TCGv_env (CPUArchState*) - ("TCGv_env", "__tcg_" + args.names()[0]), - ]) - - -def generate(events, backend, group): - if group == "root": - header = "trace/trace-root.h" - else: - header = "trace.h" - - out('/* This file is autogenerated by tracetool, do not edit. */', - '/* You must include this file after the inclusion of helper.h */', - '', - '#ifndef TRACE_%s_GENERATED_TCG_TRACERS_H' % group.upper(), - '#define TRACE_%s_GENERATED_TCG_TRACERS_H' % group.upper(), - '', - '#include "exec/helper-proto.h"', - '#include "%s"' % header, - '', - ) - - for e in events: - # just keep one of them - if "tcg-exec" not in e.properties: - continue - - out('static inline void %(name_tcg)s(%(args)s)', - '{', - name_tcg=e.original.api(e.QEMU_TRACE_TCG), - args=tracetool.vcpu.transform_args("tcg_h", e.original)) - - if "disable" not in e.properties: - args_trans = e.original.event_trans.args - args_exec = tracetool.vcpu.transform_args( - "tcg_helper_c", e.original.event_exec, "wrapper") - if "vcpu" in e.properties: - trace_cpu = e.args.names()[0] - cond = "trace_event_get_vcpu_state(%(cpu)s,"\ - " TRACE_%(id)s)"\ - % dict( - cpu=trace_cpu, - id=e.original.event_exec.name.upper()) - else: - cond = "true" - - out(' %(name_trans)s(%(argnames_trans)s);', - ' if (%(cond)s) {', - ' gen_helper_%(name_exec)s(%(argnames_exec)s);', - ' }', - name_trans=e.original.event_trans.api(e.QEMU_TRACE), - name_exec=e.original.event_exec.api(e.QEMU_TRACE), - argnames_trans=", ".join(args_trans.names()), - argnames_exec=", ".join(args_exec.names()), - cond=cond) - - out('}') - - out('', - '#endif /* TRACE_%s_GENERATED_TCG_TRACERS_H */' % group.upper()) diff --git a/scripts/tracetool/format/tcg_helper_c.py b/scripts/tracetool/format/tcg_helper_c.py deleted file mode 100644 index 72576e67d1..0000000000 --- a/scripts/tracetool/format/tcg_helper_c.py +++ /dev/null @@ -1,79 +0,0 @@ -# -*- coding: utf-8 -*- - -""" -Generate trace/generated-helpers.c. -""" - -__author__ = "Lluís Vilanova " -__copyright__ = "Copyright 2012-2017, 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, out -from tracetool.transform import * -import tracetool.vcpu - - -def vcpu_transform_args(args, mode): - assert len(args) == 1 - # NOTE: this name must be kept in sync with the one in "tcg_h" - args = Arguments([(args.types()[0], "__tcg_" + args.names()[0])]) - if mode == "code": - return Arguments([ - # Does cast from helper requirements to tracing types - ("CPUState *", "env_cpu(%s)" % args.names()[0]), - ]) - else: - args = Arguments([ - # NOTE: Current helper code uses TCGv_env (CPUArchState*) - ("CPUArchState *", args.names()[0]), - ]) - if mode == "header": - return args - elif mode == "wrapper": - return args.transform(HOST_2_TCG) - else: - assert False - - -def generate(events, backend, group): - if group == "root": - header = "trace/trace-root.h" - else: - header = "trace.h" - - events = [e for e in events - if "disable" not in e.properties] - - out('/* This file is autogenerated by tracetool, do not edit. */', - '', - '#include "qemu/osdep.h"', - '#include "cpu.h"', - '#include "exec/helper-proto.h"', - '#include "%s"' % header, - '', - ) - - for e in events: - if "tcg-exec" not in e.properties: - continue - - e_args_api = tracetool.vcpu.transform_args( - "tcg_helper_c", e.original, "header").transform( - HOST_2_TCG_COMPAT, TCG_2_TCG_HELPER_DEF) - e_args_call = tracetool.vcpu.transform_args( - "tcg_helper_c", e, "code") - - out('void %(name_tcg)s(%(args_api)s)', - '{', - # NOTE: the check was already performed at TCG-generation time - ' %(name)s(%(args_call)s);', - '}', - name_tcg="helper_%s_proxy" % e.api(), - name=e.api(e.QEMU_TRACE_NOCHECK), - args_api=e_args_api, - args_call=", ".join(e_args_call.casted()), - ) diff --git a/scripts/tracetool/format/tcg_helper_h.py b/scripts/tracetool/format/tcg_helper_h.py deleted file mode 100644 index 08554fbc85..0000000000 --- a/scripts/tracetool/format/tcg_helper_h.py +++ /dev/null @@ -1,48 +0,0 @@ -# -*- coding: utf-8 -*- - -""" -Generate trace/generated-helpers.h. -""" - -__author__ = "Lluís Vilanova " -__copyright__ = "Copyright 2012-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 out -from tracetool.transform import * -import tracetool.vcpu - - -def generate(events, backend, group): - events = [e for e in events - if "disable" not in e.properties] - - out('/* This file is autogenerated by tracetool, do not edit. */', - '', - ) - - for e in events: - if "tcg-exec" not in e.properties: - continue - - # TCG helper proxy declaration - fmt = "DEF_HELPER_FLAGS_%(argc)d(%(name)s, %(flags)svoid%(types)s)" - e_args = tracetool.vcpu.transform_args("tcg_helper_c", e.original, "header") - args = e_args.transform(HOST_2_TCG_COMPAT, HOST_2_TCG, - TCG_2_TCG_HELPER_DECL) - types = ", ".join(args.types()) - if types != "": - types = ", " + types - - flags = "TCG_CALL_NO_RWG, " - - out(fmt, - flags=flags, - argc=len(args), - name=e.api() + "_proxy", - types=types, - ) diff --git a/scripts/tracetool/format/tcg_helper_wrapper_h.py b/scripts/tracetool/format/tcg_helper_wrapper_h.py deleted file mode 100644 index 0c5a9797d1..0000000000 --- a/scripts/tracetool/format/tcg_helper_wrapper_h.py +++ /dev/null @@ -1,70 +0,0 @@ -# -*- coding: utf-8 -*- - -""" -Generate trace/generated-helpers-wrappers.h. -""" - -__author__ = "Lluís Vilanova " -__copyright__ = "Copyright 2012-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 out -from tracetool.transform import * -import tracetool.vcpu - - -def generate(events, backend, group): - events = [e for e in events - if "disable" not in e.properties] - - out('/* This file is autogenerated by tracetool, do not edit. */', - '', - '#define tcg_temp_new_nop(v) (v)', - '#define tcg_temp_free_nop(v)', - '', - ) - - for e in events: - if "tcg-exec" not in e.properties: - continue - - # tracetool.generate always transforms types to host - e_args = tracetool.vcpu.transform_args("tcg_helper_c", e.original, "wrapper") - - # mixed-type to TCG helper bridge - args_tcg_compat = e_args.transform(HOST_2_TCG_COMPAT) - - code_new = [ - "%(tcg_type)s __%(name)s = %(tcg_func)s(%(name)s);" % - {"tcg_type": transform_type(type_, HOST_2_TCG), - "tcg_func": transform_type(type_, HOST_2_TCG_TMP_NEW), - "name": name} - for (type_, name) in args_tcg_compat - ] - - code_free = [ - "%(tcg_func)s(__%(name)s);" % - {"tcg_func": transform_type(type_, HOST_2_TCG_TMP_FREE), - "name": name} - for (type_, name) in args_tcg_compat - ] - - gen_name = "gen_helper_" + e.api() - - out('static inline void %(name)s(%(args)s)', - '{', - ' %(code_new)s', - ' %(proxy_name)s(%(tmp_names)s);', - ' %(code_free)s', - '}', - name=gen_name, - args=e_args, - proxy_name=gen_name + "_proxy", - code_new="\n ".join(code_new), - code_free="\n ".join(code_free), - tmp_names=", ".join(["__%s" % name for _, name in e_args]), - ) diff --git a/scripts/tracetool/vcpu.py b/scripts/tracetool/vcpu.py index 868b4cb04c..d232cb1d06 100644 --- a/scripts/tracetool/vcpu.py +++ b/scripts/tracetool/vcpu.py @@ -19,19 +19,9 @@ 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: - # events with 'tcg-trans' and 'tcg-exec' are auto-generated from - # already-patched events - assert "tcg-trans" not in event.properties - assert "tcg-exec" not in event.properties - event.args = Arguments([("void *", "__cpu"), event.args]) - if "tcg" in event.properties: - fmt = "\"cpu=%p \"" - event.fmt = [fmt + event.fmt[0], - fmt + event.fmt[1]] - else: - fmt = "\"cpu=%p \"" - event.fmt = fmt + event.fmt + fmt = "\"cpu=%p \"" + event.fmt = fmt + event.fmt return event diff --git a/trace/meson.build b/trace/meson.build index 29f3bffd5a..26b54714d5 100644 --- a/trace/meson.build +++ b/trace/meson.build @@ -66,19 +66,6 @@ trace_events_all = custom_target('trace-events-all', install: true, install_dir: qemu_datadir) -foreach d : [ - ['generated-helpers.c', 'tcg-helper-c'], - ['generated-helpers.h', 'tcg-helper-h'], - ['generated-helpers-wrappers.h', 'tcg-helper-wrapper-h'], -] - gen = custom_target(d[0], - output: d[0], - input: meson.project_source_root() / 'trace-events', - command: [ tracetool, '--group=root', '--format=@0@'.format(d[1]), '@INPUT@', '@OUTPUT@' ], - depend_files: tracetool_depends) - specific_ss.add(when: 'CONFIG_TCG', if_true: gen) -endforeach - if 'ust' in get_option('trace_backends') trace_ust_all_h = custom_target('trace-ust-all.h', output: 'trace-ust-all.h', From 91d40327106c372760d09ffae93f91c24fcbbb6c Mon Sep 17 00:00:00 2001 From: Ivanov Arkady Date: Fri, 4 Feb 2022 20:43:30 +0000 Subject: [PATCH 391/460] plugins: add helper functions for coverage plugins MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Which provide information about: - start_code. - end_code. - entry. - path to the executable binary. Signed-off-by: Ivanov Arkady Message-Id: <163491883461.304355.8210754161847179432.stgit@pc-System-Product-Name> [AJB: reword title, better descriptions, defaults, rm export, fix include] Signed-off-by: Alex Bennée Message-Id: <20220204204335.1689602-22-alex.bennee@linaro.org> --- include/qemu/qemu-plugin.h | 34 +++++++++++++++++++++++++ plugins/api.c | 48 ++++++++++++++++++++++++++++++++++++ plugins/qemu-plugins.symbols | 4 +++ 3 files changed, 86 insertions(+) diff --git a/include/qemu/qemu-plugin.h b/include/qemu/qemu-plugin.h index 5f1017201f..535ddbf0ae 100644 --- a/include/qemu/qemu-plugin.h +++ b/include/qemu/qemu-plugin.h @@ -590,4 +590,38 @@ void qemu_plugin_outs(const char *string); */ bool qemu_plugin_bool_parse(const char *name, const char *val, bool *ret); +/** + * qemu_plugin_path_to_binary() - path to binary file being executed + * + * Return a string representing the path to the binary. For user-mode + * this is the main executable. For system emulation we currently + * return NULL. The user should g_free() the string once no longer + * needed. + */ +const char *qemu_plugin_path_to_binary(void); + +/** + * qemu_plugin_start_code() - returns start of text segment + * + * Returns the nominal start address of the main text segment in + * user-mode. Currently returns 0 for system emulation. + */ +uint64_t qemu_plugin_start_code(void); + +/** + * qemu_plugin_end_code() - returns end of text segment + * + * Returns the nominal end address of the main text segment in + * user-mode. Currently returns 0 for system emulation. + */ +uint64_t qemu_plugin_end_code(void); + +/** + * qemu_plugin_entry_code() - returns start address for module + * + * Returns the nominal entry address of the main text segment in + * user-mode. Currently returns 0 for system emulation. + */ +uint64_t qemu_plugin_entry_code(void); + #endif /* QEMU_PLUGIN_API_H */ diff --git a/plugins/api.c b/plugins/api.c index b143b09ce9..91e0c7074c 100644 --- a/plugins/api.c +++ b/plugins/api.c @@ -44,6 +44,11 @@ #ifndef CONFIG_USER_ONLY #include "qemu/plugin-memory.h" #include "hw/boards.h" +#else +#include "qemu.h" +#ifdef CONFIG_LINUX +#include "loader.h" +#endif #endif /* Uninstall and Reset handlers */ @@ -391,3 +396,46 @@ bool qemu_plugin_bool_parse(const char *name, const char *value, bool *ret) { return name && value && qapi_bool_parse(name, value, ret, NULL); } + +/* + * Binary path, start and end locations + */ +const char *qemu_plugin_path_to_binary(void) +{ + char *path = NULL; +#ifdef CONFIG_USER_ONLY + TaskState *ts = (TaskState *) current_cpu->opaque; + path = g_strdup(ts->bprm->filename); +#endif + return path; +} + +uint64_t qemu_plugin_start_code(void) +{ + uint64_t start = 0; +#ifdef CONFIG_USER_ONLY + TaskState *ts = (TaskState *) current_cpu->opaque; + start = ts->info->start_code; +#endif + return start; +} + +uint64_t qemu_plugin_end_code(void) +{ + uint64_t end = 0; +#ifdef CONFIG_USER_ONLY + TaskState *ts = (TaskState *) current_cpu->opaque; + end = ts->info->end_code; +#endif + return end; +} + +uint64_t qemu_plugin_entry_code(void) +{ + uint64_t entry = 0; +#ifdef CONFIG_USER_ONLY + TaskState *ts = (TaskState *) current_cpu->opaque; + entry = ts->info->entry; +#endif + return entry; +} diff --git a/plugins/qemu-plugins.symbols b/plugins/qemu-plugins.symbols index 4834756ba3..71f6c90549 100644 --- a/plugins/qemu-plugins.symbols +++ b/plugins/qemu-plugins.symbols @@ -1,5 +1,7 @@ { qemu_plugin_bool_parse; + qemu_plugin_end_code; + qemu_plugin_entry_code; qemu_plugin_get_hwaddr; qemu_plugin_hwaddr_device_name; qemu_plugin_hwaddr_is_io; @@ -17,6 +19,7 @@ qemu_plugin_n_max_vcpus; qemu_plugin_n_vcpus; qemu_plugin_outs; + qemu_plugin_path_to_binary; qemu_plugin_register_atexit_cb; qemu_plugin_register_flush_cb; qemu_plugin_register_vcpu_exit_cb; @@ -33,6 +36,7 @@ qemu_plugin_register_vcpu_tb_exec_inline; qemu_plugin_register_vcpu_tb_trans_cb; qemu_plugin_reset; + qemu_plugin_start_code; qemu_plugin_tb_get_insn; qemu_plugin_tb_n_insns; qemu_plugin_tb_vaddr; From 227b45dc4218516262cc892b661e5438e134cf2e Mon Sep 17 00:00:00 2001 From: Ivanov Arkady Date: Fri, 4 Feb 2022 20:43:31 +0000 Subject: [PATCH 392/460] contrib/plugins: add a drcov plugin MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds the ability to generate files in drcov format. Primary goal this script is to have coverage logfiles thatwork in Lighthouse. Signed-off-by: Ivanov Arkady Message-Id: <163491884553.304355.13246023070235438959.stgit@pc-System-Product-Name> [AJB: use g_ptr_array instead of slist] Signed-off-by: Alex Bennée Message-Id: <20220204204335.1689602-23-alex.bennee@linaro.org> --- contrib/plugins/Makefile | 1 + contrib/plugins/drcov.c | 163 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 164 insertions(+) create mode 100644 contrib/plugins/drcov.c diff --git a/contrib/plugins/Makefile b/contrib/plugins/Makefile index 54ac5ccd9f..df3499f4f2 100644 --- a/contrib/plugins/Makefile +++ b/contrib/plugins/Makefile @@ -20,6 +20,7 @@ NAMES += howvec NAMES += lockstep NAMES += hwprofile NAMES += cache +NAMES += drcov SONAMES := $(addsuffix .so,$(addprefix lib,$(NAMES))) diff --git a/contrib/plugins/drcov.c b/contrib/plugins/drcov.c new file mode 100644 index 0000000000..b4a855adaf --- /dev/null +++ b/contrib/plugins/drcov.c @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2021, Ivanov Arkady + * + * Drcov - a DynamoRIO-based tool that collects coverage information + * from a binary. Primary goal this script is to have coverage log + * files that work in Lighthouse. + * + * License: GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION; + +static char header[] = "DRCOV VERSION: 2\n" + "DRCOV FLAVOR: drcov-64\n" + "Module Table: version 2, count 1\n" + "Columns: id, base, end, entry, path\n"; + +static FILE *fp; +static const char *file_name = "file.drcov.trace"; +static GMutex lock; + +typedef struct { + uint32_t start; + uint16_t size; + uint16_t mod_id; + bool exec; +} bb_entry_t; + +/* Translated blocks */ +static GPtrArray *blocks; + +static void printf_header(unsigned long count) +{ + fprintf(fp, "%s", header); + const char *path = qemu_plugin_path_to_binary(); + uint64_t start_code = qemu_plugin_start_code(); + uint64_t end_code = qemu_plugin_end_code(); + uint64_t entry = qemu_plugin_entry_code(); + fprintf(fp, "0, 0x%lx, 0x%lx, 0x%lx, %s\n", + start_code, end_code, entry, path); + fprintf(fp, "BB Table: %ld bbs\n", count); +} + +static void printf_char_array32(uint32_t data) +{ + const uint8_t *bytes = (const uint8_t *)(&data); + fwrite(bytes, sizeof(char), sizeof(data), fp); +} + +static void printf_char_array16(uint16_t data) +{ + const uint8_t *bytes = (const uint8_t *)(&data); + fwrite(bytes, sizeof(char), sizeof(data), fp); +} + + +static void printf_el(gpointer data, gpointer user_data) +{ + bb_entry_t *bb = (bb_entry_t *)data; + if (bb->exec) { + printf_char_array32(bb->start); + printf_char_array16(bb->size); + printf_char_array16(bb->mod_id); + } + g_free(bb); +} + +static void count_block(gpointer data, gpointer user_data) +{ + unsigned long *count = (unsigned long *) user_data; + bb_entry_t *bb = (bb_entry_t *)data; + if (bb->exec) { + *count = *count + 1; + } +} + +static void plugin_exit(qemu_plugin_id_t id, void *p) +{ + unsigned long count = 0; + g_mutex_lock(&lock); + g_ptr_array_foreach(blocks, count_block, &count); + + /* Print function */ + printf_header(count); + g_ptr_array_foreach(blocks, printf_el, NULL); + + /* Clear */ + g_ptr_array_free(blocks, true); + + fclose(fp); + + g_mutex_unlock(&lock); +} + +static void plugin_init(void) +{ + fp = fopen(file_name, "wb"); + blocks = g_ptr_array_sized_new(128); +} + +static void vcpu_tb_exec(unsigned int cpu_index, void *udata) +{ + bb_entry_t *bb = (bb_entry_t *) udata; + + g_mutex_lock(&lock); + bb->exec = true; + g_mutex_unlock(&lock); +} + +static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) +{ + uint64_t pc = qemu_plugin_tb_vaddr(tb); + size_t n = qemu_plugin_tb_n_insns(tb); + + g_mutex_lock(&lock); + + bb_entry_t *bb = g_new0(bb_entry_t, 1); + for (int i = 0; i < n; i++) { + bb->size += qemu_plugin_insn_size(qemu_plugin_tb_get_insn(tb, i)); + } + + bb->start = pc; + bb->mod_id = 0; + bb->exec = false; + g_ptr_array_add(blocks, bb); + + g_mutex_unlock(&lock); + qemu_plugin_register_vcpu_tb_exec_cb(tb, vcpu_tb_exec, + QEMU_PLUGIN_CB_NO_REGS, + (void *)bb); + +} + +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++) { + g_autofree char **tokens = g_strsplit(argv[i], "=", 2); + if (g_strcmp0(tokens[0], "filename") == 0) { + file_name = g_strdup(tokens[1]); + } + } + + plugin_init(); + + qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans); + qemu_plugin_register_atexit_cb(id, plugin_exit, NULL); + + return 0; +} From 39be9dd30fff4bae81ba048d93a195eabe2635ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 4 Feb 2022 20:43:32 +0000 Subject: [PATCH 393/460] tests/plugin: allow libinsn.so per-CPU counts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We won't go fully flexible but for most system emulation 8 vCPUs resolution should be enough for anybody ;-) Signed-off-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20220204204335.1689602-24-alex.bennee@linaro.org> --- tests/plugin/insn.c | 39 +++++++++++++++++++++++++++++++-------- 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/tests/plugin/insn.c b/tests/plugin/insn.c index d229fdc001..d5a0a08cb4 100644 --- a/tests/plugin/insn.c +++ b/tests/plugin/insn.c @@ -16,22 +16,33 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION; -static uint64_t insn_count; +#define MAX_CPUS 8 /* lets not go nuts */ + +typedef struct { + uint64_t last_pc; + uint64_t insn_count; +} InstructionCount; + +static InstructionCount counts[MAX_CPUS]; +static uint64_t inline_insn_count; + static bool do_inline; static bool do_size; +static bool do_frequency; static GArray *sizes; static void vcpu_insn_exec_before(unsigned int cpu_index, void *udata) { - static uint64_t last_pc; + unsigned int i = cpu_index % MAX_CPUS; + InstructionCount *c = &counts[i]; uint64_t this_pc = GPOINTER_TO_UINT(udata); - if (this_pc == last_pc) { + if (this_pc == c->last_pc) { g_autofree gchar *out = g_strdup_printf("detected repeat execution @ 0x%" PRIx64 "\n", this_pc); qemu_plugin_outs(out); } - last_pc = this_pc; - insn_count++; + c->last_pc = this_pc; + c->insn_count++; } static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) @@ -44,7 +55,7 @@ static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) if (do_inline) { qemu_plugin_register_vcpu_insn_exec_inline( - insn, QEMU_PLUGIN_INLINE_ADD_U64, &insn_count, 1); + insn, QEMU_PLUGIN_INLINE_ADD_U64, &inline_insn_count, 1); } else { uint64_t vaddr = qemu_plugin_insn_vaddr(insn); qemu_plugin_register_vcpu_insn_exec_cb( @@ -66,9 +77,9 @@ static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) static void plugin_exit(qemu_plugin_id_t id, void *p) { g_autoptr(GString) out = g_string_new(NULL); + int i; if (do_size) { - int i; for (i = 0; i <= sizes->len; i++) { unsigned long *cnt = &g_array_index(sizes, unsigned long, i); if (*cnt) { @@ -76,8 +87,20 @@ static void plugin_exit(qemu_plugin_id_t id, void *p) "len %d bytes: %ld insns\n", i, *cnt); } } + } else if (do_inline) { + g_string_append_printf(out, "insns: %" PRIu64 "\n", inline_insn_count); } else { - g_string_append_printf(out, "insns: %" PRIu64 "\n", insn_count); + uint64_t total_insns = 0; + for (i = 0; i < MAX_CPUS; i++) { + InstructionCount *c = &counts[i]; + if (c->insn_count) { + g_string_append_printf(out, "cpu %d insns: %" PRIu64 "\n", + i, c->insn_count); + total_insns += c->insn_count; + } + } + g_string_append_printf(out, "total insns: %" PRIu64 "\n", + total_insns); } qemu_plugin_outs(out->str); } From f6d1cd4d48e84fc8047f9094760e42b0980eccec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 4 Feb 2022 20:43:33 +0000 Subject: [PATCH 394/460] tests/plugins: add instruction matching to libinsn.so MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds simple instruction matching to the libinsn.so plugin which is useful for examining the execution distance between instructions. For example to track how often we flush in ARM due to TLB updates: -plugin ./tests/plugin/libinsn.so,match=tlbi which leads to output like this: 0xffffffc01019a918, 'tlbi vale1is, x1', 5702 hits, 31825 match hits, Δ+8112 since last match, 68859 avg insns/match 0xffffffc01019a918, 'tlbi vale1is, x1', 5703 hits, 56593 match hits, Δ+17712125 since last match, 33455 avg insns/match 0xffffffc01019a918, 'tlbi vale1is, x1', 5704 hits, 56594 match hits, Δ+12689 since last match, 33454 avg insns/match 0xffffffc01019a918, 'tlbi vale1is, x1', 5705 hits, 56595 match hits, Δ+12585 since last match, 33454 avg insns/match 0xffffffc01019a918, 'tlbi vale1is, x1', 5706 hits, 56596 match hits, Δ+10491 since last match, 33454 avg insns/match 0xffffffc01019a918, 'tlbi vale1is, x1', 5707 hits, 56597 match hits, Δ+4721 since last match, 33453 avg insns/match 0xffffffc01019a918, 'tlbi vale1is, x1', 5708 hits, 56598 match hits, Δ+10733 since last match, 33453 avg insns/match 0xffffffc01019a918, 'tlbi vale1is, x1', 5709 hits, 56599 match hits, Δ+61959 since last match, 33453 avg insns/match 0xffffffc01019a918, 'tlbi vale1is, x1', 5710 hits, 56600 match hits, Δ+55235 since last match, 33454 avg insns/match 0xffffffc01019a918, 'tlbi vale1is, x1', 5711 hits, 56601 match hits, Δ+54373 since last match, 33454 avg insns/match 0xffffffc01019a918, 'tlbi vale1is, x1', 5712 hits, 56602 match hits, Δ+2705 since last match, 33453 avg insns/match 0xffffffc01019a918, 'tlbi vale1is, x1', 5713 hits, 56603 match hits, Δ+17262 since last match, 33453 avg insns/match 0xffffffc01019a918, 'tlbi vale1is, x1', 5714 hits, 56604 match hits, Δ+17206 since last match, 33453 avg insns/match 0xffffffc01019a918, 'tlbi vale1is, x1', 5715 hits, 56605 match hits, Δ+28940 since last match, 33453 avg insns/match 0xffffffc01019a918, 'tlbi vale1is, x1', 5716 hits, 56606 match hits, Δ+7370 since last match, 33452 avg insns/match 0xffffffc01019a918, 'tlbi vale1is, x1', 5717 hits, 56607 match hits, Δ+7066 since last match, 33452 avg insns/match showing we do some sort of TLBI invalidation every 33 thousand instructions. Cc: Vasilev Oleg Cc: Richard Henderson Cc: Emilio Cota Signed-off-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20220204204335.1689602-25-alex.bennee@linaro.org> --- tests/plugin/insn.c | 89 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 88 insertions(+), 1 deletion(-) diff --git a/tests/plugin/insn.c b/tests/plugin/insn.c index d5a0a08cb4..cd5ea5d4ae 100644 --- a/tests/plugin/insn.c +++ b/tests/plugin/insn.c @@ -28,9 +28,25 @@ static uint64_t inline_insn_count; static bool do_inline; static bool do_size; -static bool do_frequency; static GArray *sizes; +typedef struct { + char *match_string; + uint64_t hits[MAX_CPUS]; + uint64_t last_hit[MAX_CPUS]; + uint64_t total_delta[MAX_CPUS]; + GPtrArray *history[MAX_CPUS]; +} Match; + +static GArray *matches; + +typedef struct { + Match *match; + uint64_t vaddr; + uint64_t hits; + char *disas; +} Instruction; + static void vcpu_insn_exec_before(unsigned int cpu_index, void *udata) { unsigned int i = cpu_index % MAX_CPUS; @@ -45,6 +61,37 @@ static void vcpu_insn_exec_before(unsigned int cpu_index, void *udata) c->insn_count++; } +static void vcpu_insn_matched_exec_before(unsigned int cpu_index, void *udata) +{ + unsigned int i = cpu_index % MAX_CPUS; + Instruction *insn = (Instruction *) udata; + Match *match = insn->match; + 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 = counts[i].insn_count; + uint64_t delta = icount - match->last_hit[i]; + + match->hits[i]++; + match->total_delta[i] += delta; + + g_string_append_printf(ts, + ", %"PRId64" match hits, " + "Δ+%"PRId64 " since last match," + " %"PRId64 " avg insns/match\n", + match->hits[i], delta, + match->total_delta[i] / match->hits[i]); + + match->last_hit[i] = icount; + + qemu_plugin_outs(ts->str); + + g_ptr_array_add(match->history[i], insn); +} + static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) { size_t n = qemu_plugin_tb_n_insns(tb); @@ -71,6 +118,29 @@ static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) unsigned long *cnt = &g_array_index(sizes, unsigned long, sz); (*cnt)++; } + + /* + * 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. + */ + if (matches) { + char *insn_disas = qemu_plugin_insn_disas(insn); + int j; + for (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; + qemu_plugin_register_vcpu_insn_exec_cb( + insn, vcpu_insn_matched_exec_before, + QEMU_PLUGIN_CB_NO_REGS, rec); + } + } + g_free(insn_disas); + } } } @@ -105,6 +175,21 @@ static void plugin_exit(qemu_plugin_id_t id, void *p) qemu_plugin_outs(out->str); } + +/* Add a match to the array of matches */ +static void parse_match(char *match) +{ + Match new_match = { .match_string = match }; + int i; + for (i = 0; i < MAX_CPUS; i++) { + new_match.history[i] = g_ptr_array_new(); + } + if (!matches) { + matches = g_array_new(false, true, sizeof(Match)); + } + g_array_append_val(matches, new_match); +} + QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, const qemu_info_t *info, int argc, char **argv) @@ -122,6 +207,8 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, fprintf(stderr, "boolean argument parsing failed: %s\n", opt); return -1; } + } else if (g_strcmp0(tokens[0], "match") == 0) { + parse_match(tokens[1]); } else { fprintf(stderr, "option parsing failed: %s\n", opt); return -1; From 346cd004f65edf2e55dfd8333cb0f63e08423791 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 4 Feb 2022 20:43:34 +0000 Subject: [PATCH 395/460] target/i386: use CPU_LOG_INT for IRQ servicing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I think these have been wrong since f193c7979c (do not depend on thunk.h - more log items). Fix them so as not to confuse other debugging. Signed-off-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20220204204335.1689602-26-alex.bennee@linaro.org> --- target/i386/tcg/sysemu/seg_helper.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/i386/tcg/sysemu/seg_helper.c b/target/i386/tcg/sysemu/seg_helper.c index bf3444c26b..824b9a5a26 100644 --- a/target/i386/tcg/sysemu/seg_helper.c +++ b/target/i386/tcg/sysemu/seg_helper.c @@ -167,7 +167,7 @@ bool x86_cpu_exec_interrupt(CPUState *cs, int interrupt_request) cs->interrupt_request &= ~(CPU_INTERRUPT_HARD | CPU_INTERRUPT_VIRQ); intno = cpu_get_pic_interrupt(env); - qemu_log_mask(CPU_LOG_TB_IN_ASM, + qemu_log_mask(CPU_LOG_INT, "Servicing hardware INT=0x%02x\n", intno); do_interrupt_x86_hardirq(env, intno, 1); break; @@ -175,7 +175,7 @@ bool x86_cpu_exec_interrupt(CPUState *cs, int interrupt_request) cpu_svm_check_intercept_param(env, SVM_EXIT_VINTR, 0, 0); intno = x86_ldl_phys(cs, env->vm_vmcb + offsetof(struct vmcb, control.int_vector)); - qemu_log_mask(CPU_LOG_TB_IN_ASM, + qemu_log_mask(CPU_LOG_INT, "Servicing virtual hardware INT=0x%02x\n", intno); do_interrupt_x86_hardirq(env, intno, 1); cs->interrupt_request &= ~CPU_INTERRUPT_VIRQ; From 6f15c076dae07c2438eb488d71fa332e2b8d0963 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 4 Feb 2022 20:43:35 +0000 Subject: [PATCH 396/460] plugins: move reset of plugin data to tb_start MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We can't always guarantee we get to the end of a translator loop. Although this can happen for a variety of reasons it does happen more often on x86 system emulation when an instruction spans across to an un-faulted page. This caused confusion of the instruction tracking data resulting in apparent reverse execution (at least from the plugins point of view). Fix this by moving the reset code to plugin_gen_tb_start so we always start with a clean slate. We unconditionally reset tcg_ctx->plugin_insn as the plugin_insn_append code uses this as a proxy for knowing if plugins are enabled for the current instruction. Otherwise we can hit a race where a previously instrumented thread leaves a stale value after the main thread exits and disables instrumentation. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/824 Signed-off-by: Alex Bennée Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20220204204335.1689602-27-alex.bennee@linaro.org> --- accel/tcg/plugin-gen.c | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c index 22d95fe1c3..3d0b101e34 100644 --- a/accel/tcg/plugin-gen.c +++ b/accel/tcg/plugin-gen.c @@ -854,10 +854,20 @@ static void plugin_gen_inject(const struct qemu_plugin_tb *plugin_tb) bool plugin_gen_tb_start(CPUState *cpu, const TranslationBlock *tb, bool mem_only) { - struct qemu_plugin_tb *ptb = tcg_ctx->plugin_tb; bool ret = false; if (test_bit(QEMU_PLUGIN_EV_VCPU_TB_TRANS, cpu->plugin_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); + } + } + ptb->n = 0; + ret = true; ptb->vaddr = tb->pc; @@ -868,6 +878,9 @@ bool plugin_gen_tb_start(CPUState *cpu, const TranslationBlock *tb, bool mem_onl plugin_gen_empty_callback(PLUGIN_GEN_FROM_TB); } + + tcg_ctx->plugin_insn = NULL; + return ret; } @@ -904,23 +917,19 @@ void plugin_gen_insn_end(void) plugin_gen_empty_callback(PLUGIN_GEN_AFTER_INSN); } +/* + * There are cases where we never get to finalise a translation - for + * example a page fault during translation. As a result we shouldn't + * do any clean-up here and make sure things are reset in + * plugin_gen_tb_start. + */ void plugin_gen_tb_end(CPUState *cpu) { struct qemu_plugin_tb *ptb = tcg_ctx->plugin_tb; - int i; /* collect instrumentation requests */ qemu_plugin_tb_trans_cb(cpu, ptb); /* inject the instrumentation at the appropriate places */ plugin_gen_inject(ptb); - - /* clean up */ - for (i = 0; i < PLUGIN_N_CB_SUBTYPES; i++) { - if (ptb->cbs[i]) { - g_array_set_size(ptb->cbs[i], 0); - } - } - ptb->n = 0; - tcg_ctx->plugin_insn = NULL; } From 74154d7e4a9a693313ad7639a92ff443c6258741 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Wed, 12 Jan 2022 11:27:22 +0000 Subject: [PATCH 397/460] linux-user: Remove the deprecated ppc64abi32 target MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It's likely broken, and nobody cared for picking it up again during the deprecation phase, so let's remove this now. Since this is the last entry in deprecated_targets_list, remove the related code in the configure script, too. Signed-off-by: Thomas Huth Reviewed-by: Richard Henderson Acked-by: Cédric Le Goater Acked-by: Alex Bennée Message-Id: <20211215084958.185214-1-thuth@redhat.com> Signed-off-by: Alex Bennée Message-Id: <20220112112722.3641051-32-alex.bennee@linaro.org> --- .gitlab-ci.d/buildtest.yml | 27 ----------------- configs/targets/ppc64abi32-linux-user.mak | 8 ----- configure | 29 +------------------ docs/about/deprecated.rst | 7 ----- docs/about/removed-features.rst | 8 +++++ docs/user/main.rst | 1 - linux-user/elfload.c | 4 +-- linux-user/ppc/signal.c | 11 ++----- linux-user/ppc/target_syscall.h | 4 +-- linux-user/syscall_defs.h | 6 ++-- .../dockerfiles/debian-ppc64el-cross.docker | 2 +- tests/tcg/configure.sh | 2 +- 12 files changed, 21 insertions(+), 88 deletions(-) delete mode 100644 configs/targets/ppc64abi32-linux-user.mak diff --git a/.gitlab-ci.d/buildtest.yml b/.gitlab-ci.d/buildtest.yml index 8f2a3c8f5b..0aa70213fb 100644 --- a/.gitlab-ci.d/buildtest.yml +++ b/.gitlab-ci.d/buildtest.yml @@ -473,33 +473,6 @@ tsan-build: TARGETS: x86_64-softmmu ppc64-softmmu riscv64-softmmu x86_64-linux-user MAKE_CHECK_ARGS: bench V=1 -# These targets are on the way out -build-deprecated: - extends: .native_build_job_template - needs: - job: amd64-debian-user-cross-container - variables: - IMAGE: debian-all-test-cross - CONFIGURE_ARGS: --disable-tools - MAKE_CHECK_ARGS: build-tcg - TARGETS: ppc64abi32-linux-user - artifacts: - expire_in: 2 days - paths: - - build - -# We split the check-tcg step as test failures are expected but we still -# want to catch the build breaking. -check-deprecated: - extends: .native_test_job_template - needs: - - job: build-deprecated - artifacts: true - variables: - IMAGE: debian-all-test-cross - MAKE_CHECK_ARGS: check-tcg - allow_failure: true - # gprof/gcov are GCC features build-gprof-gcov: extends: .native_build_job_template diff --git a/configs/targets/ppc64abi32-linux-user.mak b/configs/targets/ppc64abi32-linux-user.mak deleted file mode 100644 index 0945451081..0000000000 --- a/configs/targets/ppc64abi32-linux-user.mak +++ /dev/null @@ -1,8 +0,0 @@ -TARGET_ARCH=ppc64 -TARGET_ABI32=y -TARGET_BASE_ARCH=ppc -TARGET_ABI_DIR=ppc -TARGET_SYSTBL_ABI=common,nospu,32 -TARGET_SYSTBL=syscall.tbl -TARGET_WORDS_BIGENDIAN=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 diff --git a/configure b/configure index dfb9019b24..3a29eff5cc 100755 --- a/configure +++ b/configure @@ -1252,8 +1252,6 @@ if eval test -z "\${cross_cc_$cpu}"; then fi default_target_list="" -deprecated_targets_list=ppc64abi32-linux-user -deprecated_features="" mak_wilds="" if [ "$linux_user" != no ]; then @@ -1281,16 +1279,6 @@ if [ "$bsd_user" = "yes" ]; then mak_wilds="${mak_wilds} $source_path/configs/targets/*-bsd-user.mak" fi -# If the user doesn't explicitly specify a deprecated target we will -# skip it. -if test -z "$target_list"; then - if test -z "$target_list_exclude"; then - target_list_exclude="$deprecated_targets_list" - else - target_list_exclude="$target_list_exclude,$deprecated_targets_list" - fi -fi - for config in $mak_wilds; do target="$(basename "$config" .mak)" if echo "$target_list_exclude" | grep -vq "$target"; then @@ -1309,11 +1297,9 @@ Standard options: --prefix=PREFIX install in PREFIX [$prefix] --interp-prefix=PREFIX where to find shared libraries, etc. use %M for cpu name [$interp_prefix] - --target-list=LIST set target list (default: build all non-deprecated) + --target-list=LIST set target list (default: build all) $(echo Available targets: $default_target_list | \ fold -s -w 53 | sed -e 's/^/ /') -$(echo Deprecated targets: $deprecated_targets_list | \ - fold -s -w 53 | sed -e 's/^/ /') --target-list-exclude=LIST exclude a set of targets from the default target-list Advanced options (experts only): @@ -1797,13 +1783,6 @@ else done fi -for target in $target_list; do - # if a deprecated target is enabled we note it here - if echo "$deprecated_targets_list" | grep -q "$target"; then - add_to deprecated_features $target - fi -done - # see if system emulation was really requested case " $target_list " in *"-softmmu "*) softmmu=yes @@ -3830,12 +3809,6 @@ else fi fi -if test -n "${deprecated_features}"; then - echo "Warning, deprecated features enabled." - echo "Please see docs/about/deprecated.rst" - echo " features: ${deprecated_features}" -fi - # Save the configure command line for later reuse. cat <config.status #!/bin/sh diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index 47a594a3b6..25b1fb8677 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -403,13 +403,6 @@ The above, converted to the current supported format:: linux-user mode CPUs -------------------- -``ppc64abi32`` CPUs (since 5.2) -''''''''''''''''''''''''''''''' - -The ``ppc64abi32`` architecture has a number of issues which regularly -trip up our CI testing and is suspected to be quite broken. For that -reason the maintainers strongly suspect no one actually uses it. - MIPS ``I7200`` CPU (since 5.2) '''''''''''''''''''''''''''''' diff --git a/docs/about/removed-features.rst b/docs/about/removed-features.rst index b0156e0f25..cb0575fd49 100644 --- a/docs/about/removed-features.rst +++ b/docs/about/removed-features.rst @@ -601,6 +601,14 @@ the upstream Linux kernel in 2018, and it has also been dropped from glibc, so there is no new Linux development taking place with this architecture. For running the old binaries, you can use older versions of QEMU. +``ppc64abi32`` CPUs (removed in 7.0) +'''''''''''''''''''''''''''''''''''' + +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. + + TCG introspection features -------------------------- diff --git a/docs/user/main.rst b/docs/user/main.rst index e08d4be63b..6f2ffa080f 100644 --- a/docs/user/main.rst +++ b/docs/user/main.rst @@ -166,7 +166,6 @@ Other binaries - user mode (PowerPC) - * ``qemu-ppc64abi32`` TODO. * ``qemu-ppc64`` TODO. * ``qemu-ppc`` TODO. diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 99829faf89..9628a38361 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -718,7 +718,7 @@ static inline void init_thread(struct target_pt_regs *regs, #define ELF_MACHINE PPC_ELF_MACHINE #define ELF_START_MMAP 0x80000000 -#if defined(TARGET_PPC64) && !defined(TARGET_ABI32) +#if defined(TARGET_PPC64) #define elf_check_arch(x) ( (x) == EM_PPC64 ) @@ -870,7 +870,7 @@ static uint32_t get_elf_hwcap2(void) static inline void init_thread(struct target_pt_regs *_regs, struct image_info *infop) { _regs->gpr[1] = infop->start_stack; -#if defined(TARGET_PPC64) && !defined(TARGET_ABI32) +#if defined(TARGET_PPC64) if (get_ppc64_abi(infop) < 2) { uint64_t val; get_user_u64(val, infop->entry + 8); diff --git a/linux-user/ppc/signal.c b/linux-user/ppc/signal.c index 176c9d8503..ec0b9c0df3 100644 --- a/linux-user/ppc/signal.c +++ b/linux-user/ppc/signal.c @@ -477,9 +477,7 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, int i, err = 0; #if defined(TARGET_PPC64) struct target_sigcontext *sc = 0; -#if !defined(TARGET_ABI32) struct image_info *image = ((TaskState *)thread_cpu->opaque)->info; -#endif #endif rt_sf_addr = get_sigframe(ka, env, sizeof(*rt_sf)); @@ -530,7 +528,7 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, env->gpr[5] = (target_ulong) h2g(&rt_sf->uc); env->gpr[6] = (target_ulong) h2g(rt_sf); -#if defined(TARGET_PPC64) && !defined(TARGET_ABI32) +#if defined(TARGET_PPC64) if (get_ppc64_abi(image) < 2) { /* ELFv1 PPC64 function pointers are pointers to OPD entries. */ struct target_func_ptr *handler = @@ -562,7 +560,7 @@ sigsegv: } -#if !defined(TARGET_PPC64) || defined(TARGET_ABI32) +#if !defined(TARGET_PPC64) long do_sigreturn(CPUPPCState *env) { struct target_sigcontext *sc = NULL; @@ -575,12 +573,9 @@ long do_sigreturn(CPUPPCState *env) if (!lock_user_struct(VERIFY_READ, sc, sc_addr, 1)) goto sigsegv; -#if defined(TARGET_PPC64) - set.sig[0] = sc->oldmask + ((uint64_t)(sc->_unused[3]) << 32); -#else __get_user(set.sig[0], &sc->oldmask); __get_user(set.sig[1], &sc->_unused[3]); -#endif + target_to_host_sigset_internal(&blocked, &set); set_sigmask(&blocked); diff --git a/linux-user/ppc/target_syscall.h b/linux-user/ppc/target_syscall.h index 8b364697d4..7df9118937 100644 --- a/linux-user/ppc/target_syscall.h +++ b/linux-user/ppc/target_syscall.h @@ -36,7 +36,7 @@ struct target_pt_regs { abi_ulong link; abi_ulong xer; abi_ulong ccr; -#if defined(TARGET_PPC64) && !defined(TARGET_ABI32) +#if defined(TARGET_PPC64) abi_ulong softe; #else abi_ulong mq; /* 601 only (not used at present) */ @@ -58,7 +58,7 @@ struct target_revectored_struct { * flags masks */ -#if defined(TARGET_PPC64) && !defined(TARGET_ABI32) +#if defined(TARGET_PPC64) #ifdef TARGET_WORDS_BIGENDIAN #define UNAME_MACHINE "ppc64" #else diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h index 78607effe8..4587b62ac9 100644 --- a/linux-user/syscall_defs.h +++ b/linux-user/syscall_defs.h @@ -1558,7 +1558,7 @@ struct target_stat64 { struct target_stat { abi_ulong st_dev; abi_ulong st_ino; -#if defined(TARGET_PPC64) && !defined(TARGET_ABI32) +#if defined(TARGET_PPC64) abi_ulong st_nlink; unsigned int st_mode; #else @@ -1579,12 +1579,12 @@ struct target_stat { abi_ulong target_st_ctime_nsec; abi_ulong __unused4; abi_ulong __unused5; -#if defined(TARGET_PPC64) && !defined(TARGET_ABI32) +#if defined(TARGET_PPC64) abi_ulong __unused6; #endif }; -#if !defined(TARGET_PPC64) || defined(TARGET_ABI32) +#if !defined(TARGET_PPC64) #define TARGET_HAS_STRUCT_STAT64 struct QEMU_PACKED target_stat64 { unsigned long long st_dev; diff --git a/tests/docker/dockerfiles/debian-ppc64el-cross.docker b/tests/docker/dockerfiles/debian-ppc64el-cross.docker index 1146a06be6..5de12b01cd 100644 --- a/tests/docker/dockerfiles/debian-ppc64el-cross.docker +++ b/tests/docker/dockerfiles/debian-ppc64el-cross.docker @@ -16,7 +16,7 @@ RUN apt update && \ # Specify the cross prefix for this image (see tests/docker/common.rc) ENV QEMU_CONFIGURE_OPTS --cross-prefix=powerpc64le-linux-gnu- -ENV DEF_TARGET_LIST ppc64-softmmu,ppc64-linux-user,ppc64abi32-linux-user +ENV DEF_TARGET_LIST ppc64-softmmu,ppc64-linux-user # Install extra libraries to increase code coverage RUN apt update && \ diff --git a/tests/tcg/configure.sh b/tests/tcg/configure.sh index 309335a2bd..763e9b6ad8 100755 --- a/tests/tcg/configure.sh +++ b/tests/tcg/configure.sh @@ -167,7 +167,7 @@ for target in $target_list; do container_image=debian-nios2-cross container_cross_cc=nios2-linux-gnu-gcc ;; - ppc-*|ppc64abi32-*) + ppc-*) container_hosts=x86_64 container_image=debian-powerpc-test-cross container_cross_cc=powerpc-linux-gnu-gcc-10 From 514f9f8eb64bfd5d6c15024db93f83bd81998de5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Tue, 8 Feb 2022 12:43:33 +0000 Subject: [PATCH 398/460] include/exec: fix softmmu version of TARGET_ABI_FMT_lx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TARGET_ABI_FMT_lx isn't available for softmmu which causes confusion when trying to print. As abi_ptr == target_ulong use its format string instead. Signed-off-by: Alex Bennée Reviewed-by: Peter Maydell --- include/exec/cpu_ldst.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/exec/cpu_ldst.h b/include/exec/cpu_ldst.h index a878fd0105..da987fe8ad 100644 --- a/include/exec/cpu_ldst.h +++ b/include/exec/cpu_ldst.h @@ -121,7 +121,7 @@ static inline bool guest_range_valid_untagged(abi_ulong start, abi_ulong len) }) #else typedef target_ulong abi_ptr; -#define TARGET_ABI_FMT_ptr TARGET_ABI_FMT_lx +#define TARGET_ABI_FMT_ptr TARGET_FMT_lx #endif uint32_t cpu_ldub_data(CPUArchState *env, abi_ptr ptr); From aba8205be0707b9d108e32254e186ba88107a869 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Thu, 3 Feb 2022 15:05:33 +0100 Subject: [PATCH 399/460] block: Lock AioContext for drain_end in blockdev-reopen bdrv_subtree_drained_end() requires the caller to hold the AioContext lock for the drained node. Not doing this for nodes outside of the main AioContext leads to crashes when AIO_WAIT_WHILE() needs to wait and tries to temporarily release the lock. Fixes: 3908b7a8994fa5ef7a89aa58cd5a02fc58141592 Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=2046659 Reported-by: Qing Wang Signed-off-by: Kevin Wolf Message-Id: <20220203140534.36522-2-kwolf@redhat.com> Reviewed-by: Hanna Reitz Signed-off-by: Kevin Wolf --- blockdev.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/blockdev.c b/blockdev.c index 8197165bb5..42e098b458 100644 --- a/blockdev.c +++ b/blockdev.c @@ -3530,6 +3530,7 @@ void qmp_blockdev_reopen(BlockdevOptionsList *reopen_list, Error **errp) { BlockReopenQueue *queue = NULL; GSList *drained = NULL; + GSList *p; /* Add each one of the BDS that we want to reopen to the queue */ for (; reopen_list != NULL; reopen_list = reopen_list->next) { @@ -3579,7 +3580,15 @@ void qmp_blockdev_reopen(BlockdevOptionsList *reopen_list, Error **errp) fail: bdrv_reopen_queue_free(queue); - g_slist_free_full(drained, (GDestroyNotify) bdrv_subtree_drained_end); + for (p = drained; p; p = p->next) { + BlockDriverState *bs = p->data; + AioContext *ctx = bdrv_get_aio_context(bs); + + aio_context_acquire(ctx); + bdrv_subtree_drained_end(bs); + aio_context_release(ctx); + } + g_slist_free(drained); } void qmp_blockdev_del(const char *node_name, Error **errp) From ee810602376125ca0e0afd6b7c715e13740978ea Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Thu, 3 Feb 2022 15:05:34 +0100 Subject: [PATCH 400/460] iotests: Test blockdev-reopen with iothreads and throttling The 'throttle' block driver implements .bdrv_co_drain_end, so blockdev-reopen will have to wait for it to complete in the polling loop at the end of qmp_blockdev_reopen(). This makes AIO_WAIT_WHILE() release the AioContext lock, which causes a crash if the lock hasn't correctly been taken. Signed-off-by: Kevin Wolf Message-Id: <20220203140534.36522-3-kwolf@redhat.com> Reviewed-by: Hanna Reitz Signed-off-by: Kevin Wolf --- tests/qemu-iotests/245 | 36 +++++++++++++++++++++++++++++++++--- tests/qemu-iotests/245.out | 4 ++-- 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/tests/qemu-iotests/245 b/tests/qemu-iotests/245 index 24ac43f70e..8cbed7821b 100755 --- a/tests/qemu-iotests/245 +++ b/tests/qemu-iotests/245 @@ -1138,12 +1138,13 @@ class TestBlockdevReopen(iotests.QMPTestCase): self.assertEqual(self.get_node('hd1'), None) self.assert_qmp(self.get_node('hd2'), 'ro', True) - def run_test_iothreads(self, iothread_a, iothread_b, errmsg = None): - opts = hd_opts(0) + def run_test_iothreads(self, iothread_a, iothread_b, errmsg = None, + opts_a = None, opts_b = None): + opts = opts_a or hd_opts(0) result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) self.assert_qmp(result, 'return', {}) - opts2 = hd_opts(2) + opts2 = opts_b or hd_opts(2) result = self.vm.qmp('blockdev-add', conv_keys = False, **opts2) self.assert_qmp(result, 'return', {}) @@ -1194,6 +1195,35 @@ class TestBlockdevReopen(iotests.QMPTestCase): def test_iothreads_switch_overlay(self): self.run_test_iothreads('', 'iothread0') + def test_iothreads_with_throttling(self): + # Create a throttle-group object + opts = { 'qom-type': 'throttle-group', 'id': 'group0', + 'limits': { 'iops-total': 1000 } } + result = self.vm.qmp('object-add', conv_keys = False, **opts) + self.assert_qmp(result, 'return', {}) + + # Options with a throttle filter between format and protocol + opts = [ + { + 'driver': iotests.imgfmt, + 'node-name': f'hd{idx}', + 'file' : { + 'node-name': f'hd{idx}-throttle', + 'driver': 'throttle', + 'throttle-group': 'group0', + 'file': { + 'driver': 'file', + 'node-name': f'hd{idx}-file', + 'filename': hd_path[idx], + }, + }, + } + for idx in (0, 2) + ] + + self.run_test_iothreads('iothread0', 'iothread0', None, + opts[0], opts[1]) + if __name__ == '__main__': iotests.activate_logging() iotests.main(supported_fmts=["qcow2"], diff --git a/tests/qemu-iotests/245.out b/tests/qemu-iotests/245.out index 4eced19294..a4e04a3266 100644 --- a/tests/qemu-iotests/245.out +++ b/tests/qemu-iotests/245.out @@ -17,8 +17,8 @@ read 1/1 bytes at offset 262152 read 1/1 bytes at offset 262160 1 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -............... +................ ---------------------------------------------------------------------- -Ran 25 tests +Ran 26 tests OK From 3ce1fc16bad9c3f8b7b10b451a224d6d76e5c551 Mon Sep 17 00:00:00 2001 From: Hanna Reitz Date: Fri, 4 Feb 2022 12:10:06 +0100 Subject: [PATCH 401/460] block/nbd: Delete reconnect delay timer when done We start the reconnect delay timer to cancel the reconnection attempt after a while. Once nbd_co_do_establish_connection() has returned, this attempt is over, and we no longer need the timer. Delete it before returning from nbd_reconnect_attempt(), so that it does not persist beyond the I/O request that was paused for reconnecting; we do not want it to fire in a drained section, because all sort of things can happen in such a section (e.g. the AioContext might be changed, and we do not want the timer to fire in the wrong context; or the BDS might even be deleted, and so the timer CB would access already-freed data). Reviewed-by: Vladimir Sementsov-Ogievskiy Signed-off-by: Hanna Reitz Signed-off-by: Vladimir Sementsov-Ogievskiy --- block/nbd.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/block/nbd.c b/block/nbd.c index 63dbfa807d..16cd7fef77 100644 --- a/block/nbd.c +++ b/block/nbd.c @@ -381,6 +381,13 @@ static coroutine_fn void nbd_reconnect_attempt(BDRVNBDState *s) } nbd_co_do_establish_connection(s->bs, NULL); + + /* + * The reconnect attempt is done (maybe successfully, maybe not), so + * we no longer need this timer. Delete it so it will not outlive + * this I/O request (so draining removes all timers). + */ + reconnect_delay_timer_del(s); } static coroutine_fn int nbd_receive_replies(BDRVNBDState *s, uint64_t handle) From 717be9644b25341517a8a3377a5f773fa902810b Mon Sep 17 00:00:00 2001 From: Hanna Reitz Date: Fri, 4 Feb 2022 12:10:07 +0100 Subject: [PATCH 402/460] block/nbd: Delete open timer when done We start the open timer to cancel the connection attempt after a while. Once nbd_do_establish_connection() has returned, the attempt is over, and we no longer need the timer. Delete it before returning from nbd_open(), so that it does not persist for longer. It has no use after nbd_open(), and just like the reconnect delay timer, it might well be dangerous if it were to fire afterwards. Reviewed-by: Vladimir Sementsov-Ogievskiy Signed-off-by: Hanna Reitz Signed-off-by: Vladimir Sementsov-Ogievskiy --- block/nbd.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/block/nbd.c b/block/nbd.c index 16cd7fef77..5ff8a57314 100644 --- a/block/nbd.c +++ b/block/nbd.c @@ -1885,11 +1885,19 @@ static int nbd_open(BlockDriverState *bs, QDict *options, int flags, goto fail; } + /* + * The connect attempt is done, so we no longer need this timer. + * Delete it, because we do not want it to be around when this node + * is drained or closed. + */ + open_timer_del(s); + nbd_client_connection_enable_retry(s->conn); return 0; fail: + open_timer_del(s); nbd_clear_bdrvstate(bs); return ret; } From 8a39c381e5e407d2fe5500324323f90a8540fa90 Mon Sep 17 00:00:00 2001 From: Hanna Reitz Date: Fri, 4 Feb 2022 12:10:08 +0100 Subject: [PATCH 403/460] block/nbd: Assert there are no timers when closed Our two timers must not remain armed beyond nbd_clear_bdrvstate(), or they will access freed data when they fire. This patch is separate from the patches that actually fix the issue (HEAD^^ and HEAD^) so that you can run the associated regression iotest (281) on a configuration that reproducibly exposes the bug. Reviewed-by: Vladimir Sementsov-Ogievskiy Signed-off-by: Hanna Reitz Signed-off-by: Vladimir Sementsov-Ogievskiy --- block/nbd.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/block/nbd.c b/block/nbd.c index 5ff8a57314..dc6c3f3bbc 100644 --- a/block/nbd.c +++ b/block/nbd.c @@ -110,6 +110,10 @@ static void nbd_clear_bdrvstate(BlockDriverState *bs) yank_unregister_instance(BLOCKDEV_YANK_INSTANCE(bs->node_name)); + /* Must not leave timers behind that would access freed data */ + assert(!s->reconnect_delay_timer); + assert(!s->open_timer); + object_unref(OBJECT(s->tlscreds)); qapi_free_SocketAddress(s->saddr); s->saddr = NULL; From 091dc7b2b5553a529bff9a7bf9ad3bc85bc5bdcd Mon Sep 17 00:00:00 2001 From: Hanna Reitz Date: Fri, 4 Feb 2022 12:10:09 +0100 Subject: [PATCH 404/460] iotests.py: Add QemuStorageDaemon class This is a rather simple class that allows creating a QSD instance running in the background and stopping it when no longer needed. The __del__ handler is a safety net for when something goes so wrong in a test that e.g. the tearDown() method is not called (e.g. setUp() launches the QSD, but then launching a VM fails). We do not want the QSD to continue running after the test has failed, so __del__() will take care to kill it. Reviewed-by: Vladimir Sementsov-Ogievskiy Signed-off-by: Hanna Reitz Signed-off-by: Vladimir Sementsov-Ogievskiy --- tests/qemu-iotests/iotests.py | 40 +++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py index 8cdb381f2a..6ba65eb1ff 100644 --- a/tests/qemu-iotests/iotests.py +++ b/tests/qemu-iotests/iotests.py @@ -73,6 +73,8 @@ if os.environ.get('QEMU_NBD_OPTIONS'): qemu_prog = os.environ.get('QEMU_PROG', 'qemu') qemu_opts = os.environ.get('QEMU_OPTIONS', '').strip().split(' ') +qsd_prog = os.environ.get('QSD_PROG', 'qemu-storage-daemon') + gdb_qemu_env = os.environ.get('GDB_OPTIONS') qemu_gdb = [] if gdb_qemu_env: @@ -345,6 +347,44 @@ class QemuIoInteractive: return self._read_output() +class QemuStorageDaemon: + def __init__(self, *args: str, instance_id: str = 'a'): + assert '--pidfile' not in args + self.pidfile = os.path.join(test_dir, f'qsd-{instance_id}-pid') + all_args = [qsd_prog] + list(args) + ['--pidfile', self.pidfile] + + # Cannot use with here, we want the subprocess to stay around + # pylint: disable=consider-using-with + self._p = subprocess.Popen(all_args) + while not os.path.exists(self.pidfile): + if self._p.poll() is not None: + cmd = ' '.join(all_args) + raise RuntimeError( + 'qemu-storage-daemon terminated with exit code ' + + f'{self._p.returncode}: {cmd}') + + time.sleep(0.01) + + with open(self.pidfile, encoding='utf-8') as f: + self._pid = int(f.read().strip()) + + assert self._pid == self._p.pid + + def stop(self, kill_signal=15): + self._p.send_signal(kill_signal) + self._p.wait() + self._p = None + + try: + os.remove(self.pidfile) + except OSError: + pass + + def __del__(self): + if self._p is not None: + self.stop(kill_signal=9) + + def qemu_nbd(*args): '''Run qemu-nbd in daemon mode and return the parent's exit code''' return subprocess.call(qemu_nbd_args + ['--fork'] + list(args)) From eaf1e85d4ddefdbd197f393fa9c5acc7ba8133b0 Mon Sep 17 00:00:00 2001 From: Hanna Reitz Date: Fri, 4 Feb 2022 12:10:10 +0100 Subject: [PATCH 405/460] iotests/281: Test lingering timers Prior to "block/nbd: Delete reconnect delay timer when done" and "block/nbd: Delete open timer when done", both of those timers would remain scheduled even after successfully (re-)connecting to the server, and they would not even be deleted when the BDS is deleted. This test constructs exactly this situation: (1) Configure an @open-timeout, so the open timer is armed, and (2) Configure a @reconnect-delay and trigger a reconnect situation (which succeeds immediately), so the reconnect delay timer is armed. Then we immediately delete the BDS, and sleep for longer than the @open-timeout and @reconnect-delay. Prior to said patches, this caused one (or both) of the timer CBs to access already-freed data. Accessing freed data may or may not crash, so this test can produce false successes, but I do not know how to show the problem in a better or more reliable way. If you run this test on "block/nbd: Assert there are no timers when closed" and without the fix patches mentioned above, you should reliably see an assertion failure. (But all other tests that use the reconnect delay timer (264 and 277) will fail in that configuration, too; as will nbd-reconnect-on-open, which uses the open timer.) Remove this test from the quick group because of the two second sleep this patch introduces. (I decided to put this test case into 281, because the main bug this series addresses is in the interaction of the NBD block driver and I/O threads, which is precisely the scope of 281. The test case for that other bug will also be put into the test class added here. Also, excuse the test class's name, I couldn't come up with anything better. The "yield" part will make sense two patches from now.) Reviewed-by: Vladimir Sementsov-Ogievskiy Signed-off-by: Hanna Reitz Signed-off-by: Vladimir Sementsov-Ogievskiy --- tests/qemu-iotests/281 | 79 +++++++++++++++++++++++++++++++++++++- tests/qemu-iotests/281.out | 4 +- 2 files changed, 79 insertions(+), 4 deletions(-) diff --git a/tests/qemu-iotests/281 b/tests/qemu-iotests/281 index 318e333939..4fb3cd30dd 100755 --- a/tests/qemu-iotests/281 +++ b/tests/qemu-iotests/281 @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# group: rw quick +# group: rw # # Test cases for blockdev + IOThread interactions # @@ -20,8 +20,9 @@ # import os +import time import iotests -from iotests import qemu_img +from iotests import qemu_img, QemuStorageDaemon image_len = 64 * 1024 * 1024 @@ -243,6 +244,80 @@ class TestBlockdevBackupAbort(iotests.QMPTestCase): # Hangs on failure, we expect this error. self.assert_qmp(result, 'error/class', 'GenericError') +# Test for RHBZ#2033626 +class TestYieldingAndTimers(iotests.QMPTestCase): + sock = os.path.join(iotests.sock_dir, 'nbd.sock') + qsd = None + + def setUp(self): + self.create_nbd_export() + + # Simple VM with an NBD block device connected to the NBD export + # provided by the QSD + self.vm = iotests.VM() + self.vm.add_blockdev('nbd,node-name=nbd,server.type=unix,' + + f'server.path={self.sock},export=exp,' + + 'reconnect-delay=1,open-timeout=1') + + self.vm.launch() + + def tearDown(self): + self.stop_nbd_export() + self.vm.shutdown() + + def test_timers_with_blockdev_del(self): + # The NBD BDS will have had an active open timer, because setUp() gave + # a positive value for @open-timeout. It should be gone once the BDS + # has been opened. + # (But there used to be a bug where it remained active, which will + # become important below.) + + # Stop and restart the NBD server, and do some I/O on the client to + # trigger a reconnect and start the reconnect delay timer + self.stop_nbd_export() + self.create_nbd_export() + + result = self.vm.qmp('human-monitor-command', + command_line='qemu-io nbd "write 0 512"') + self.assert_qmp(result, 'return', '') + + # Reconnect is done, so the reconnect delay timer should be gone. + # (This is similar to how the open timer should be gone after open, + # and similarly there used to be a bug where it was not gone.) + + # Delete the BDS to see whether both timers are gone. If they are not, + # they will remain active, fire later, and then access freed data. + # (Or, with "block/nbd: Assert there are no timers when closed" + # applied, the assertions added in that patch will fail.) + result = self.vm.qmp('blockdev-del', node_name='nbd') + self.assert_qmp(result, 'return', {}) + + # Give the timers some time to fire (both have a timeout of 1 s). + # (Sleeping in an iotest may ring some alarm bells, but note that if + # the timing is off here, the test will just always pass. If we kill + # the VM too early, then we just kill the timers before they can fire, + # thus not see the error, and so the test will pass.) + time.sleep(2) + + def create_nbd_export(self): + assert self.qsd is None + + # Simple NBD export of a null-co BDS + self.qsd = QemuStorageDaemon( + '--blockdev', + 'null-co,node-name=null,read-zeroes=true', + + '--nbd-server', + f'addr.type=unix,addr.path={self.sock}', + + '--export', + 'nbd,id=exp,node-name=null,name=exp,writable=true' + ) + + def stop_nbd_export(self): + self.qsd.stop() + self.qsd = None + if __name__ == '__main__': iotests.main(supported_fmts=['qcow2'], supported_protocols=['file'], diff --git a/tests/qemu-iotests/281.out b/tests/qemu-iotests/281.out index 89968f35d7..914e3737bd 100644 --- a/tests/qemu-iotests/281.out +++ b/tests/qemu-iotests/281.out @@ -1,5 +1,5 @@ -.... +..... ---------------------------------------------------------------------- -Ran 4 tests +Ran 5 tests OK From e15f3a66c830e3fce99c9d56c493c2f7078a1225 Mon Sep 17 00:00:00 2001 From: Hanna Reitz Date: Fri, 4 Feb 2022 12:10:11 +0100 Subject: [PATCH 406/460] block/nbd: Move s->ioc on AioContext change s->ioc must always be attached to the NBD node's AioContext. If that context changes, s->ioc must be attached to the new context. Buglink: https://bugzilla.redhat.com/show_bug.cgi?id=2033626 Reviewed-by: Vladimir Sementsov-Ogievskiy Signed-off-by: Hanna Reitz Signed-off-by: Vladimir Sementsov-Ogievskiy --- block/nbd.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/block/nbd.c b/block/nbd.c index dc6c3f3bbc..5853d85d60 100644 --- a/block/nbd.c +++ b/block/nbd.c @@ -2055,6 +2055,42 @@ static void nbd_cancel_in_flight(BlockDriverState *bs) nbd_co_establish_connection_cancel(s->conn); } +static void nbd_attach_aio_context(BlockDriverState *bs, + AioContext *new_context) +{ + BDRVNBDState *s = bs->opaque; + + /* The open_timer is used only during nbd_open() */ + assert(!s->open_timer); + + /* + * The reconnect_delay_timer is scheduled in I/O paths when the + * connection is lost, to cancel the reconnection attempt after a + * given time. Once this attempt is done (successfully or not), + * nbd_reconnect_attempt() ensures the timer is deleted before the + * respective I/O request is resumed. + * Since the AioContext can only be changed when a node is drained, + * the reconnect_delay_timer cannot be active here. + */ + assert(!s->reconnect_delay_timer); + + if (s->ioc) { + qio_channel_attach_aio_context(s->ioc, new_context); + } +} + +static void nbd_detach_aio_context(BlockDriverState *bs) +{ + BDRVNBDState *s = bs->opaque; + + assert(!s->open_timer); + assert(!s->reconnect_delay_timer); + + if (s->ioc) { + qio_channel_detach_aio_context(s->ioc); + } +} + static BlockDriver bdrv_nbd = { .format_name = "nbd", .protocol_name = "nbd", @@ -2078,6 +2114,9 @@ static BlockDriver bdrv_nbd = { .bdrv_dirname = nbd_dirname, .strong_runtime_opts = nbd_strong_runtime_opts, .bdrv_cancel_in_flight = nbd_cancel_in_flight, + + .bdrv_attach_aio_context = nbd_attach_aio_context, + .bdrv_detach_aio_context = nbd_detach_aio_context, }; static BlockDriver bdrv_nbd_tcp = { @@ -2103,6 +2142,9 @@ static BlockDriver bdrv_nbd_tcp = { .bdrv_dirname = nbd_dirname, .strong_runtime_opts = nbd_strong_runtime_opts, .bdrv_cancel_in_flight = nbd_cancel_in_flight, + + .bdrv_attach_aio_context = nbd_attach_aio_context, + .bdrv_detach_aio_context = nbd_detach_aio_context, }; static BlockDriver bdrv_nbd_unix = { @@ -2128,6 +2170,9 @@ static BlockDriver bdrv_nbd_unix = { .bdrv_dirname = nbd_dirname, .strong_runtime_opts = nbd_strong_runtime_opts, .bdrv_cancel_in_flight = nbd_cancel_in_flight, + + .bdrv_attach_aio_context = nbd_attach_aio_context, + .bdrv_detach_aio_context = nbd_detach_aio_context, }; static void bdrv_nbd_init(void) From 8cfbe929e8c26050f0a4580a1606a370a947d4ce Mon Sep 17 00:00:00 2001 From: Hanna Reitz Date: Fri, 4 Feb 2022 12:10:12 +0100 Subject: [PATCH 407/460] iotests/281: Let NBD connection yield in iothread Put an NBD block device into an I/O thread, and then read data from it, hoping that the NBD connection will yield during that read. When it does, the coroutine must be reentered in the block device's I/O thread, which will only happen if the NBD block driver attaches the connection's QIOChannel to the new AioContext. It did not do that after 4ddb5d2fde ("block/nbd: drop connection_co") and prior to "block/nbd: Move s->ioc on AioContext change", which would cause an assertion failure. To improve our chances of yielding, the NBD server is throttled to reading 64 kB/s, and the NBD client reads 128 kB, so it should yield at some point. Reviewed-by: Vladimir Sementsov-Ogievskiy Signed-off-by: Hanna Reitz Signed-off-by: Vladimir Sementsov-Ogievskiy --- tests/qemu-iotests/281 | 28 +++++++++++++++++++++++++--- tests/qemu-iotests/281.out | 4 ++-- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/tests/qemu-iotests/281 b/tests/qemu-iotests/281 index 4fb3cd30dd..5e1339bd75 100755 --- a/tests/qemu-iotests/281 +++ b/tests/qemu-iotests/281 @@ -253,8 +253,9 @@ class TestYieldingAndTimers(iotests.QMPTestCase): self.create_nbd_export() # Simple VM with an NBD block device connected to the NBD export - # provided by the QSD + # provided by the QSD, and an (initially unused) iothread self.vm = iotests.VM() + self.vm.add_object('iothread,id=iothr') self.vm.add_blockdev('nbd,node-name=nbd,server.type=unix,' + f'server.path={self.sock},export=exp,' + 'reconnect-delay=1,open-timeout=1') @@ -299,19 +300,40 @@ class TestYieldingAndTimers(iotests.QMPTestCase): # thus not see the error, and so the test will pass.) time.sleep(2) + def test_yield_in_iothread(self): + # Move the NBD node to the I/O thread; the NBD block driver should + # attach the connection's QIOChannel to that thread's AioContext, too + result = self.vm.qmp('x-blockdev-set-iothread', + node_name='nbd', iothread='iothr') + self.assert_qmp(result, 'return', {}) + + # Do some I/O that will be throttled by the QSD, so that the network + # connection hopefully will yield here. When it is resumed, it must + # then be resumed in the I/O thread's AioContext. + result = self.vm.qmp('human-monitor-command', + command_line='qemu-io nbd "read 0 128K"') + self.assert_qmp(result, 'return', '') + def create_nbd_export(self): assert self.qsd is None - # Simple NBD export of a null-co BDS + # Export a throttled null-co BDS: Reads are throttled (max 64 kB/s), + # writes are not. self.qsd = QemuStorageDaemon( + '--object', + 'throttle-group,id=thrgr,x-bps-read=65536,x-bps-read-max=65536', + '--blockdev', 'null-co,node-name=null,read-zeroes=true', + '--blockdev', + 'throttle,node-name=thr,file=null,throttle-group=thrgr', + '--nbd-server', f'addr.type=unix,addr.path={self.sock}', '--export', - 'nbd,id=exp,node-name=null,name=exp,writable=true' + 'nbd,id=exp,node-name=thr,name=exp,writable=true' ) def stop_nbd_export(self): diff --git a/tests/qemu-iotests/281.out b/tests/qemu-iotests/281.out index 914e3737bd..3f8a935a08 100644 --- a/tests/qemu-iotests/281.out +++ b/tests/qemu-iotests/281.out @@ -1,5 +1,5 @@ -..... +...... ---------------------------------------------------------------------- -Ran 5 tests +Ran 6 tests OK From fdb8541b2e4f6ff60f435fbb5a5e1df20c275a86 Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Wed, 9 Feb 2022 20:15:58 +0100 Subject: [PATCH 408/460] hw/block/fdc-isa: Respect QOM properties when building AML MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Other ISA devices such as serial-isa use the properties in their build_aml functions. fdc-isa not using them is probably an oversight. Signed-off-by: Bernhard Beschow Message-Id: <20220209191558.30393-1-shentey@gmail.com> Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Kevin Wolf --- hw/block/fdc-isa.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/hw/block/fdc-isa.c b/hw/block/fdc-isa.c index 3bf64e0665..ab663dce93 100644 --- a/hw/block/fdc-isa.c +++ b/hw/block/fdc-isa.c @@ -216,6 +216,7 @@ int cmos_get_fd_drive_type(FloppyDriveType fd0) static void fdc_isa_build_aml(ISADevice *isadev, Aml *scope) { + FDCtrlISABus *isa = ISA_FDC(isadev); Aml *dev; Aml *crs; int i; @@ -227,11 +228,13 @@ static void fdc_isa_build_aml(ISADevice *isadev, Aml *scope) }; crs = aml_resource_template(); - aml_append(crs, aml_io(AML_DECODE16, 0x03F2, 0x03F2, 0x00, 0x04)); - aml_append(crs, aml_io(AML_DECODE16, 0x03F7, 0x03F7, 0x00, 0x01)); - aml_append(crs, aml_irq_no_flags(6)); aml_append(crs, - aml_dma(AML_COMPATIBILITY, AML_NOTBUSMASTER, AML_TRANSFER8, 2)); + aml_io(AML_DECODE16, isa->iobase + 2, isa->iobase + 2, 0x00, 0x04)); + aml_append(crs, + aml_io(AML_DECODE16, isa->iobase + 7, isa->iobase + 7, 0x00, 0x01)); + aml_append(crs, aml_irq_no_flags(isa->irq)); + aml_append(crs, + aml_dma(AML_COMPATIBILITY, AML_NOTBUSMASTER, AML_TRANSFER8, isa->dma)); dev = aml_device("FDC0"); aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0700"))); From f3e5a17593b972a9a6079ccf7677b4389d74d5a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 14 Jan 2022 13:08:57 +0800 Subject: [PATCH 409/460] hw/net/vmxnet3: Log guest-triggerable errors using LOG_GUEST_ERROR MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The "Interrupt Cause" register (VMXNET3_REG_ICR) is read-only. Write accesses are ignored. Log them with as LOG_GUEST_ERROR instead of aborting: [R +0.239743] writeq 0xe0002031 0x46291a5a55460800 ERROR:hw/net/vmxnet3.c:1819:vmxnet3_io_bar1_write: code should not be reached Thread 1 "qemu-system-i38" received signal SIGABRT, Aborted. (gdb) bt #3 0x74c397d3 in __GI_abort () at abort.c:79 #4 0x76d3cd4c in g_assertion_message (domain=, file=, line=, func=, message=) at ../glib/gtestutils.c:3223 #5 0x76d9d45f in g_assertion_message_expr (domain=0x0, file=0x59fc2e53 "hw/net/vmxnet3.c", line=1819, func=0x59fc11e0 <__func__.vmxnet3_io_bar1_write> "vmxnet3_io_bar1_write", expr=) at ../glib/gtestutils.c:3249 #6 0x57e80a3a in vmxnet3_io_bar1_write (opaque=0x62814100, addr=56, val=70, size=4) at hw/net/vmxnet3.c:1819 #7 0x58c2d894 in memory_region_write_accessor (mr=0x62816b90, addr=56, value=0x7fff9450, size=4, shift=0, mask=4294967295, attrs=...) at softmmu/memory.c:492 #8 0x58c2d1d2 in access_with_adjusted_size (addr=56, value=0x7fff9450, size=1, access_size_min=4, access_size_max=4, access_fn= 0x58c2d290 , mr=0x62816b90, attrs=...) at softmmu/memory.c:554 #9 0x58c2bae7 in memory_region_dispatch_write (mr=0x62816b90, addr=56, data=70, op=MO_8, attrs=...) at softmmu/memory.c:1504 #10 0x58bfd034 in flatview_write_continue (fv=0x606000181700, addr=0xe0002038, attrs=..., ptr=0x7fffb9e0, len=1, addr1=56, l=1, mr=0x62816b90) at softmmu/physmem.c:2782 #11 0x58beba00 in flatview_write (fv=0x606000181700, addr=0xe0002031, attrs=..., buf=0x7fffb9e0, len=8) at softmmu/physmem.c:2822 #12 0x58beb589 in address_space_write (as=0x608000015f20, addr=0xe0002031, attrs=..., buf=0x7fffb9e0, len=8) at softmmu/physmem.c:2914 Reported-by: Dike Reported-by: Duhao <504224090@qq.com> BugLink: https://bugzilla.redhat.com/show_bug.cgi?id=2032932 Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Jason Wang --- hw/net/vmxnet3.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/hw/net/vmxnet3.c b/hw/net/vmxnet3.c index f65af4e9ef..0b7acf7f89 100644 --- a/hw/net/vmxnet3.c +++ b/hw/net/vmxnet3.c @@ -1816,7 +1816,9 @@ vmxnet3_io_bar1_write(void *opaque, case VMXNET3_REG_ICR: VMW_CBPRN("Write BAR1 [VMXNET3_REG_ICR] = %" PRIx64 ", size %d", val, size); - g_assert_not_reached(); + qemu_log_mask(LOG_GUEST_ERROR, + "%s: write to read-only register VMXNET3_REG_ICR\n", + TYPE_VMXNET3); break; /* Event Cause Register */ From 41bcea7b2c99b8aec613beb62a6cdeb371a09449 Mon Sep 17 00:00:00 2001 From: Peter Foley Date: Fri, 14 Jan 2022 13:08:58 +0800 Subject: [PATCH 410/460] net/tap: Set return code on failure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Match the other error handling in this function. Fixes: e7b347d0bf6 ("net: detect errors from probing vnet hdr flag for TAP devices") Reviewed-by: Patrick Venture Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Peter Foley Signed-off-by: Jason Wang --- net/tap.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/tap.c b/net/tap.c index f716be3e3f..c5cbeaa7a2 100644 --- a/net/tap.c +++ b/net/tap.c @@ -900,6 +900,7 @@ int net_init_tap(const Netdev *netdev, const char *name, if (i == 0) { vnet_hdr = tap_probe_vnet_hdr(fd, errp); if (vnet_hdr < 0) { + ret = -1; goto free_fail; } } else if (vnet_hdr != tap_probe_vnet_hdr(fd, NULL)) { From e29919c93d19118610d64de9deb9c223024c0bc6 Mon Sep 17 00:00:00 2001 From: Peter Foley Date: Fri, 14 Jan 2022 13:08:59 +0800 Subject: [PATCH 411/460] net: Fix uninitialized data usage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit e.g. 1109 15:16:20.151506 Uninitialized bytes in ioctl_common_pre at offset 0 inside [0x7ffc516af9b8, 4) 1109 15:16:20.151659 ==588974==WARNING: MemorySanitizer: use-of-uninitialized-value 1109 15:16:20.312923 #0 0x5639b88acb21 in tap_probe_vnet_hdr_len third_party/qemu/net/tap-linux.c:183:9 1109 15:16:20.312952 #1 0x5639b88afd66 in net_tap_fd_init third_party/qemu/net/tap.c:409:9 1109 15:16:20.312954 #2 0x5639b88b2d1b in net_init_tap_one third_party/qemu/net/tap.c:681:19 1109 15:16:20.312956 #3 0x5639b88b16a8 in net_init_tap third_party/qemu/net/tap.c:912:13 1109 15:16:20.312957 #4 0x5639b8890175 in net_client_init1 third_party/qemu/net/net.c:1110:9 1109 15:16:20.312958 #5 0x5639b888f912 in net_client_init third_party/qemu/net/net.c:1208:15 1109 15:16:20.312960 #6 0x5639b8894aa5 in net_param_nic third_party/qemu/net/net.c:1588:11 1109 15:16:20.312961 #7 0x5639b900cd18 in qemu_opts_foreach third_party/qemu/util/qemu-option.c:1135:14 1109 15:16:20.312962 #8 0x5639b889393c in net_init_clients third_party/qemu/net/net.c:1612:9 1109 15:16:20.312964 #9 0x5639b717aaf3 in qemu_create_late_backends third_party/qemu/softmmu/vl.c:1962:5 1109 15:16:20.312965 #10 0x5639b717aaf3 in qemu_init third_party/qemu/softmmu/vl.c:3694:5 1109 15:16:20.312967 #11 0x5639b71083b8 in main third_party/qemu/softmmu/main.c:49:5 1109 15:16:20.312968 #12 0x7f464de1d8d2 in __libc_start_main (/usr/grte/v5/lib64/libc.so.6+0x628d2) 1109 15:16:20.312969 #13 0x5639b6bbd389 in _start /usr/grte/v5/debug-src/src/csu/../sysdeps/x86_64/start.S:120 1109 15:16:20.312970 1109 15:16:20.312975 Uninitialized value was stored to memory at 1109 15:16:20.313393 #0 0x5639b88acbee in tap_probe_vnet_hdr_len third_party/qemu/net/tap-linux.c 1109 15:16:20.313396 #1 0x5639b88afd66 in net_tap_fd_init third_party/qemu/net/tap.c:409:9 1109 15:16:20.313398 #2 0x5639b88b2d1b in net_init_tap_one third_party/qemu/net/tap.c:681:19 1109 15:16:20.313399 #3 0x5639b88b16a8 in net_init_tap third_party/qemu/net/tap.c:912:13 1109 15:16:20.313400 #4 0x5639b8890175 in net_client_init1 third_party/qemu/net/net.c:1110:9 1109 15:16:20.313401 #5 0x5639b888f912 in net_client_init third_party/qemu/net/net.c:1208:15 1109 15:16:20.313403 #6 0x5639b8894aa5 in net_param_nic third_party/qemu/net/net.c:1588:11 1109 15:16:20.313404 #7 0x5639b900cd18 in qemu_opts_foreach third_party/qemu/util/qemu-option.c:1135:14 1109 15:16:20.313405 #8 0x5639b889393c in net_init_clients third_party/qemu/net/net.c:1612:9 1109 15:16:20.313407 #9 0x5639b717aaf3 in qemu_create_late_backends third_party/qemu/softmmu/vl.c:1962:5 1109 15:16:20.313408 #10 0x5639b717aaf3 in qemu_init third_party/qemu/softmmu/vl.c:3694:5 1109 15:16:20.313409 #11 0x5639b71083b8 in main third_party/qemu/softmmu/main.c:49:5 1109 15:16:20.313410 #12 0x7f464de1d8d2 in __libc_start_main (/usr/grte/v5/lib64/libc.so.6+0x628d2) 1109 15:16:20.313412 #13 0x5639b6bbd389 in _start /usr/grte/v5/debug-src/src/csu/../sysdeps/x86_64/start.S:120 1109 15:16:20.313413 1109 15:16:20.313417 Uninitialized value was stored to memory at 1109 15:16:20.313791 #0 0x5639b88affbd in net_tap_fd_init third_party/qemu/net/tap.c:400:26 1109 15:16:20.313826 #1 0x5639b88b2d1b in net_init_tap_one third_party/qemu/net/tap.c:681:19 1109 15:16:20.313829 #2 0x5639b88b16a8 in net_init_tap third_party/qemu/net/tap.c:912:13 1109 15:16:20.313831 #3 0x5639b8890175 in net_client_init1 third_party/qemu/net/net.c:1110:9 1109 15:16:20.313836 #4 0x5639b888f912 in net_client_init third_party/qemu/net/net.c:1208:15 1109 15:16:20.313838 #5 0x5639b8894aa5 in net_param_nic third_party/qemu/net/net.c:1588:11 1109 15:16:20.313839 #6 0x5639b900cd18 in qemu_opts_foreach third_party/qemu/util/qemu-option.c:1135:14 1109 15:16:20.313841 #7 0x5639b889393c in net_init_clients third_party/qemu/net/net.c:1612:9 1109 15:16:20.313843 #8 0x5639b717aaf3 in qemu_create_late_backends third_party/qemu/softmmu/vl.c:1962:5 1109 15:16:20.313844 #9 0x5639b717aaf3 in qemu_init third_party/qemu/softmmu/vl.c:3694:5 1109 15:16:20.313845 #10 0x5639b71083b8 in main third_party/qemu/softmmu/main.c:49:5 1109 15:16:20.313846 #11 0x7f464de1d8d2 in __libc_start_main (/usr/grte/v5/lib64/libc.so.6+0x628d2) 1109 15:16:20.313847 #12 0x5639b6bbd389 in _start /usr/grte/v5/debug-src/src/csu/../sysdeps/x86_64/start.S:120 1109 15:16:20.313849 1109 15:16:20.313851 Uninitialized value was created by an allocation of 'ifr' in the stack frame of function 'tap_probe_vnet_hdr' 1109 15:16:20.313855 #0 0x5639b88ac680 in tap_probe_vnet_hdr third_party/qemu/net/tap-linux.c:151 1109 15:16:20.313856 1109 15:16:20.313878 SUMMARY: MemorySanitizer: use-of-uninitialized-value third_party/qemu/net/tap-linux.c:183:9 in tap_probe_vnet_hdr_len Fixes: dc69004c7d8 ("net: move tap_probe_vnet_hdr() to tap-linux.c") Reviewed-by: Hao Wu Reviewed-by: Patrick Venture Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Peter Foley Signed-off-by: Jason Wang --- net/tap-linux.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/tap-linux.c b/net/tap-linux.c index 9584769740..5e70b93037 100644 --- a/net/tap-linux.c +++ b/net/tap-linux.c @@ -150,6 +150,7 @@ void tap_set_sndbuf(int fd, const NetdevTapOptions *tap, Error **errp) int tap_probe_vnet_hdr(int fd, Error **errp) { struct ifreq ifr; + memset(&ifr, 0, sizeof(ifr)); if (ioctl(fd, TUNGETIFF, &ifr) != 0) { /* TUNGETIFF is available since kernel v2.6.27 */ From a5f038e2c5e262ded63869a9e4bdf4951821e480 Mon Sep 17 00:00:00 2001 From: Zhang Chen Date: Fri, 14 Jan 2022 13:09:00 +0800 Subject: [PATCH 412/460] net/colo-compare.c: Optimize compare order for performance COLO-compare use the glib function g_queue_find_custom to dump another VM's networking packet to compare. But this function always start find from the queue->head(here is the newest packet), It will reduce the success rate of comparison. So this patch reversed the order of the queues for performance. Signed-off-by: Zhang Chen Reported-by: leirao Signed-off-by: Jason Wang --- net/colo-compare.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/net/colo-compare.c b/net/colo-compare.c index b966e7e514..216de5a12b 100644 --- a/net/colo-compare.c +++ b/net/colo-compare.c @@ -197,7 +197,7 @@ static void colo_compare_inconsistency_notify(CompareState *s) /* Use restricted to colo_insert_packet() */ static gint seq_sorter(Packet *a, Packet *b, gpointer data) { - return a->tcp_seq - b->tcp_seq; + return b->tcp_seq - a->tcp_seq; } static void fill_pkt_tcp_info(void *data, uint32_t *max_ack) @@ -421,13 +421,13 @@ pri: if (g_queue_is_empty(&conn->primary_list)) { return; } - ppkt = g_queue_pop_head(&conn->primary_list); + ppkt = g_queue_pop_tail(&conn->primary_list); sec: if (g_queue_is_empty(&conn->secondary_list)) { - g_queue_push_head(&conn->primary_list, ppkt); + g_queue_push_tail(&conn->primary_list, ppkt); return; } - spkt = g_queue_pop_head(&conn->secondary_list); + spkt = g_queue_pop_tail(&conn->secondary_list); if (ppkt->tcp_seq == ppkt->seq_end) { colo_release_primary_pkt(s, ppkt); @@ -458,7 +458,7 @@ sec: } } if (!ppkt) { - g_queue_push_head(&conn->secondary_list, spkt); + g_queue_push_tail(&conn->secondary_list, spkt); goto pri; } } @@ -477,7 +477,7 @@ sec: if (mark == COLO_COMPARE_FREE_PRIMARY) { conn->compare_seq = ppkt->seq_end; colo_release_primary_pkt(s, ppkt); - g_queue_push_head(&conn->secondary_list, spkt); + g_queue_push_tail(&conn->secondary_list, spkt); goto pri; } else if (mark == COLO_COMPARE_FREE_SECONDARY) { conn->compare_seq = spkt->seq_end; @@ -490,8 +490,8 @@ sec: goto pri; } } else { - g_queue_push_head(&conn->primary_list, ppkt); - g_queue_push_head(&conn->secondary_list, spkt); + g_queue_push_tail(&conn->primary_list, ppkt); + g_queue_push_tail(&conn->secondary_list, spkt); #ifdef DEBUG_COLO_PACKETS qemu_hexdump(stderr, "colo-compare ppkt", ppkt->data, ppkt->size); @@ -673,7 +673,7 @@ static void colo_compare_packet(CompareState *s, Connection *conn, while (!g_queue_is_empty(&conn->primary_list) && !g_queue_is_empty(&conn->secondary_list)) { - pkt = g_queue_pop_head(&conn->primary_list); + pkt = g_queue_pop_tail(&conn->primary_list); result = g_queue_find_custom(&conn->secondary_list, pkt, (GCompareFunc)HandlePacket); @@ -689,7 +689,7 @@ static void colo_compare_packet(CompareState *s, Connection *conn, * timeout, it will trigger a checkpoint request. */ trace_colo_compare_main("packet different"); - g_queue_push_head(&conn->primary_list, pkt); + g_queue_push_tail(&conn->primary_list, pkt); colo_compare_inconsistency_notify(s); break; @@ -819,7 +819,7 @@ static int compare_chr_send(CompareState *s, entry->buf = g_malloc(size); memcpy(entry->buf, buf, size); } - g_queue_push_head(&sendco->send_list, entry); + g_queue_push_tail(&sendco->send_list, entry); if (sendco->done) { sendco->co = qemu_coroutine_create(_compare_chr_send, sendco); @@ -1347,7 +1347,7 @@ static void colo_flush_packets(void *opaque, void *user_data) Packet *pkt = NULL; while (!g_queue_is_empty(&conn->primary_list)) { - pkt = g_queue_pop_head(&conn->primary_list); + pkt = g_queue_pop_tail(&conn->primary_list); compare_chr_send(s, pkt->data, pkt->size, @@ -1357,7 +1357,7 @@ static void colo_flush_packets(void *opaque, void *user_data) packet_destroy_partial(pkt, NULL); } while (!g_queue_is_empty(&conn->secondary_list)) { - pkt = g_queue_pop_head(&conn->secondary_list); + pkt = g_queue_pop_tail(&conn->secondary_list); packet_destroy(pkt, NULL); } } From 09313cdb44b2ccec218bc85f39073954f91ee9ea Mon Sep 17 00:00:00 2001 From: Zhang Chen Date: Fri, 14 Jan 2022 13:09:01 +0800 Subject: [PATCH 413/460] net/colo-compare.c: Update the default value comments Make the comments consistent with the REGULAR_PACKET_CHECK_MS. Signed-off-by: Zhang Chen Signed-off-by: Jason Wang --- net/colo-compare.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/colo-compare.c b/net/colo-compare.c index 216de5a12b..62554b5b3c 100644 --- a/net/colo-compare.c +++ b/net/colo-compare.c @@ -1267,7 +1267,7 @@ static void colo_compare_complete(UserCreatable *uc, Error **errp) } if (!s->expired_scan_cycle) { - /* Set default value to 3000 MS */ + /* Set default value to 1000 MS */ s->expired_scan_cycle = REGULAR_PACKET_CHECK_MS; } From 611382968069f54914e3cfff30f2a3b92c6219cd Mon Sep 17 00:00:00 2001 From: Rao Lei Date: Fri, 14 Jan 2022 13:09:02 +0800 Subject: [PATCH 414/460] net/filter: Optimize filter_send to coroutine This patch is to improve the logic of QEMU main thread sleep code in qemu_chr_write_buffer() where it can be blocked and can't run other coroutines during COLO IO stress test. Our approach is to put filter_send() in a coroutine. In this way, filter_send() will call qemu_coroutine_yield() in qemu_co_sleep_ns(), so that it can be scheduled out and QEMU main thread has opportunity to run other tasks. Signed-off-by: Lei Rao Signed-off-by: Zhang Chen Reviewed-by: Li Zhijian Reviewed-by: Zhang Chen Signed-off-by: Jason Wang --- net/filter-mirror.c | 66 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 53 insertions(+), 13 deletions(-) diff --git a/net/filter-mirror.c b/net/filter-mirror.c index f20240cc9f..34a63b5dbb 100644 --- a/net/filter-mirror.c +++ b/net/filter-mirror.c @@ -20,6 +20,7 @@ #include "chardev/char-fe.h" #include "qemu/iov.h" #include "qemu/sockets.h" +#include "block/aio-wait.h" #define TYPE_FILTER_MIRROR "filter-mirror" typedef struct MirrorState MirrorState; @@ -42,20 +43,21 @@ struct MirrorState { bool vnet_hdr; }; -static int filter_send(MirrorState *s, - const struct iovec *iov, - int iovcnt) +typedef struct FilterSendCo { + MirrorState *s; + char *buf; + ssize_t size; + bool done; + int ret; +} FilterSendCo; + +static int _filter_send(MirrorState *s, + char *buf, + ssize_t size) { NetFilterState *nf = NETFILTER(s); int ret = 0; - ssize_t size = 0; uint32_t len = 0; - char *buf; - - size = iov_size(iov, iovcnt); - if (!size) { - return 0; - } len = htonl(size); ret = qemu_chr_fe_write_all(&s->chr_out, (uint8_t *)&len, sizeof(len)); @@ -80,10 +82,7 @@ static int filter_send(MirrorState *s, } } - buf = g_malloc(size); - iov_to_buf(iov, iovcnt, 0, buf, size); ret = qemu_chr_fe_write_all(&s->chr_out, (uint8_t *)buf, size); - g_free(buf); if (ret != size) { goto err; } @@ -94,6 +93,47 @@ err: return ret < 0 ? ret : -EIO; } +static void coroutine_fn filter_send_co(void *opaque) +{ + FilterSendCo *data = opaque; + + data->ret = _filter_send(data->s, data->buf, data->size); + data->done = true; + g_free(data->buf); + aio_wait_kick(); +} + +static int filter_send(MirrorState *s, + const struct iovec *iov, + int iovcnt) +{ + ssize_t size = iov_size(iov, iovcnt); + char *buf = NULL; + + if (!size) { + return 0; + } + + buf = g_malloc(size); + iov_to_buf(iov, iovcnt, 0, buf, size); + + FilterSendCo data = { + .s = s, + .size = size, + .buf = buf, + .ret = 0, + }; + + Coroutine *co = qemu_coroutine_create(filter_send_co, &data); + qemu_coroutine_enter(co); + + while (!data.done) { + aio_poll(qemu_get_aio_context(), true); + } + + return data.ret; +} + static void redirector_to_filter(NetFilterState *nf, const uint8_t *buf, int len) From 870374214e4cc122f086f55732f1b17ec320132e Mon Sep 17 00:00:00 2001 From: Nick Hudson Date: Sat, 12 Feb 2022 09:44:17 +0000 Subject: [PATCH 415/460] hw/net: e1000e: Clear ICR on read when using non MSI-X interrupts In section 7.4.3 of the 82574 datasheet it states that "In systems that do not support MSI-X, reading the ICR register clears it's bits..." Some OSes rely on this. Signed-off-by: Nick Hudson Signed-off-by: Jason Wang --- hw/net/e1000e_core.c | 5 +++++ hw/net/trace-events | 1 + 2 files changed, 6 insertions(+) diff --git a/hw/net/e1000e_core.c b/hw/net/e1000e_core.c index 8ae6fb7e14..2c51089a82 100644 --- a/hw/net/e1000e_core.c +++ b/hw/net/e1000e_core.c @@ -2607,6 +2607,11 @@ e1000e_mac_icr_read(E1000ECore *core, int index) core->mac[ICR] = 0; } + if (!msix_enabled(core->owner)) { + trace_e1000e_irq_icr_clear_nonmsix_icr_read(); + core->mac[ICR] = 0; + } + if ((core->mac[ICR] & E1000_ICR_ASSERTED) && (core->mac[CTRL_EXT] & E1000_CTRL_EXT_IAME)) { trace_e1000e_irq_icr_clear_iame(); diff --git a/hw/net/trace-events b/hw/net/trace-events index 643338f610..4c0ec3fda1 100644 --- a/hw/net/trace-events +++ b/hw/net/trace-events @@ -221,6 +221,7 @@ e1000e_irq_write_ics(uint32_t val) "Adding ICR bits 0x%x" e1000e_irq_icr_process_iame(void) "Clearing IMS bits due to IAME" e1000e_irq_read_ics(uint32_t ics) "Current ICS: 0x%x" e1000e_irq_read_ims(uint32_t ims) "Current IMS: 0x%x" +e1000e_irq_icr_clear_nonmsix_icr_read(void) "Clearing ICR on read due to non MSI-X int" e1000e_irq_icr_read_entry(uint32_t icr) "Starting ICR read. Current ICR: 0x%x" e1000e_irq_icr_read_exit(uint32_t icr) "Ending ICR read. Current ICR: 0x%x" e1000e_irq_icr_clear_zero_ims(void) "Clearing ICR on read due to zero IMS" From 9d6267b240c114d1a3cd314a08fd6e1339d34b83 Mon Sep 17 00:00:00 2001 From: Thomas Jansen Date: Sat, 12 Feb 2022 19:56:41 +0100 Subject: [PATCH 416/460] net/eth: Don't consider ESP to be an IPv6 option header The IPv6 option headers all have in common that they start with some common fields, in particular the type of the next header followed by the extention header length. This is used to traverse the list of the options. The ESP header does not follow that format, which can break the IPv6 option header traversal code in eth_parse_ipv6_hdr(). The effect of that is that network interfaces such as vmxnet3 that use the following call chain eth_is_ip6_extension_header_type eth_parse_ipv6_hdr net_tx_pkt_parse_headers net_tx_pkt_parse vmxnet3_process_tx_queue to send packets from the VM out to the host will drop packets of the following structure: Ethernet-Header(IPv6-Header(ESP(encrypted data))) Note that not all types of network interfaces use the net_tx_pkt_parse function though, leading to inconsistent behavior regarding sending those packets. The e1000 network interface for example does not suffer from this limitation. By not considering ESP to be an IPv6 header we can allow sending those packets out to the host on all types of network interfaces. Fixes: 75020a702151 ("Common definitions for VMWARE devices") Resolves: https://gitlab.com/qemu-project/qemu/-/issues/149 Buglink: https://bugs.launchpad.net/qemu/+bug/1758091 Signed-off-by: Thomas Jansen Signed-off-by: Jason Wang --- net/eth.c | 1 - 1 file changed, 1 deletion(-) diff --git a/net/eth.c b/net/eth.c index fe876d1a55..f074b2f9f3 100644 --- a/net/eth.c +++ b/net/eth.c @@ -389,7 +389,6 @@ eth_is_ip6_extension_header_type(uint8_t hdr_type) case IP6_HOP_BY_HOP: case IP6_ROUTING: case IP6_FRAGMENT: - case IP6_ESP: case IP6_AUTHENTICATION: case IP6_DESTINATON: case IP6_MOBILITY: From 736b01642d85be832385063f278fe7cd4ffb5221 Mon Sep 17 00:00:00 2001 From: Klaus Jensen Date: Fri, 17 Dec 2021 10:44:01 +0100 Subject: [PATCH 417/460] hw/nvme: fix CVE-2021-3929 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixes CVE-2021-3929 "locally" by denying DMA to the iomem of the device itself. This still allows DMA to MMIO regions of other devices (e.g. doing P2P DMA to the controller memory buffer of another NVMe device). Fixes: CVE-2021-3929 Reported-by: Qiuhao Li Reviewed-by: Keith Busch Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Klaus Jensen --- hw/nvme/ctrl.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c index 1f62116af9..37681a9759 100644 --- a/hw/nvme/ctrl.c +++ b/hw/nvme/ctrl.c @@ -357,6 +357,24 @@ static inline void *nvme_addr_to_pmr(NvmeCtrl *n, hwaddr addr) return memory_region_get_ram_ptr(&n->pmr.dev->mr) + (addr - n->pmr.cba); } +static inline bool nvme_addr_is_iomem(NvmeCtrl *n, hwaddr addr) +{ + hwaddr hi, lo; + + /* + * The purpose of this check is to guard against invalid "local" access to + * the iomem (i.e. controller registers). Thus, we check against the range + * covered by the 'bar0' MemoryRegion since that is currently composed of + * two subregions (the NVMe "MBAR" and the MSI-X table/pba). Note, however, + * that if the device model is ever changed to allow the CMB to be located + * in BAR0 as well, then this must be changed. + */ + lo = n->bar0.addr; + hi = lo + int128_get64(n->bar0.size); + + return addr >= lo && addr < hi; +} + static int nvme_addr_read(NvmeCtrl *n, hwaddr addr, void *buf, int size) { hwaddr hi = addr + size - 1; @@ -614,6 +632,10 @@ static uint16_t nvme_map_addr(NvmeCtrl *n, NvmeSg *sg, hwaddr addr, size_t len) trace_pci_nvme_map_addr(addr, len); + if (nvme_addr_is_iomem(n, addr)) { + return NVME_DATA_TRAS_ERROR; + } + if (nvme_addr_is_cmb(n, addr)) { cmb = true; } else if (nvme_addr_is_pmr(n, addr)) { From e080ce8676e9097184304a2ed11bf95443c0e547 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 11 Nov 2021 16:45:51 +0100 Subject: [PATCH 418/460] hw/nvme/ctrl: Have nvme_addr_write() take const buffer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 'buf' argument is not modified, so better pass it as const type. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Klaus Jensen Signed-off-by: Klaus Jensen --- 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 37681a9759..12e1fcda7c 100644 --- a/hw/nvme/ctrl.c +++ b/hw/nvme/ctrl.c @@ -395,7 +395,7 @@ static int nvme_addr_read(NvmeCtrl *n, hwaddr addr, void *buf, int size) return pci_dma_read(&n->parent_obj, addr, buf, size); } -static int nvme_addr_write(NvmeCtrl *n, hwaddr addr, void *buf, int size) +static int nvme_addr_write(NvmeCtrl *n, hwaddr addr, const void *buf, int size) { hwaddr hi = addr + size - 1; if (hi < addr) { From 8d3a17be6f556a996ab9404bead7fc58758c21eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 11 Nov 2021 16:45:52 +0100 Subject: [PATCH 419/460] hw/nvme/ctrl: Pass buffers as 'void *' types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These buffers can be anything, not an array of chars, so use the 'void *' type for them. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Klaus Jensen Signed-off-by: Klaus Jensen --- hw/nvme/ctrl.c | 10 +++++----- hw/nvme/nvme.h | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c index 12e1fcda7c..4344405e59 100644 --- a/hw/nvme/ctrl.c +++ b/hw/nvme/ctrl.c @@ -1162,7 +1162,7 @@ static uint16_t nvme_tx_interleaved(NvmeCtrl *n, NvmeSg *sg, uint8_t *ptr, return NVME_SUCCESS; } -static uint16_t nvme_tx(NvmeCtrl *n, NvmeSg *sg, uint8_t *ptr, uint32_t len, +static uint16_t nvme_tx(NvmeCtrl *n, NvmeSg *sg, void *ptr, uint32_t len, NvmeTxDirection dir) { assert(sg->flags & NVME_SG_ALLOC); @@ -1199,7 +1199,7 @@ static uint16_t nvme_tx(NvmeCtrl *n, NvmeSg *sg, uint8_t *ptr, uint32_t len, return NVME_SUCCESS; } -static inline uint16_t nvme_c2h(NvmeCtrl *n, uint8_t *ptr, uint32_t len, +static inline uint16_t nvme_c2h(NvmeCtrl *n, void *ptr, uint32_t len, NvmeRequest *req) { uint16_t status; @@ -1212,7 +1212,7 @@ static inline uint16_t nvme_c2h(NvmeCtrl *n, uint8_t *ptr, uint32_t len, return nvme_tx(n, &req->sg, ptr, len, NVME_TX_DIRECTION_FROM_DEVICE); } -static inline uint16_t nvme_h2c(NvmeCtrl *n, uint8_t *ptr, uint32_t len, +static inline uint16_t nvme_h2c(NvmeCtrl *n, void *ptr, uint32_t len, NvmeRequest *req) { uint16_t status; @@ -1225,7 +1225,7 @@ static inline uint16_t nvme_h2c(NvmeCtrl *n, uint8_t *ptr, uint32_t len, return nvme_tx(n, &req->sg, ptr, len, NVME_TX_DIRECTION_TO_DEVICE); } -uint16_t nvme_bounce_data(NvmeCtrl *n, uint8_t *ptr, uint32_t len, +uint16_t nvme_bounce_data(NvmeCtrl *n, void *ptr, uint32_t len, NvmeTxDirection dir, NvmeRequest *req) { NvmeNamespace *ns = req->ns; @@ -1241,7 +1241,7 @@ uint16_t nvme_bounce_data(NvmeCtrl *n, uint8_t *ptr, uint32_t len, return nvme_tx(n, &req->sg, ptr, len, dir); } -uint16_t nvme_bounce_mdata(NvmeCtrl *n, uint8_t *ptr, uint32_t len, +uint16_t nvme_bounce_mdata(NvmeCtrl *n, void *ptr, uint32_t len, NvmeTxDirection dir, NvmeRequest *req) { NvmeNamespace *ns = req->ns; diff --git a/hw/nvme/nvme.h b/hw/nvme/nvme.h index 83ffabade4..38f3ebf7f6 100644 --- a/hw/nvme/nvme.h +++ b/hw/nvme/nvme.h @@ -495,9 +495,9 @@ static inline uint16_t nvme_cid(NvmeRequest *req) } void nvme_attach_ns(NvmeCtrl *n, NvmeNamespace *ns); -uint16_t nvme_bounce_data(NvmeCtrl *n, uint8_t *ptr, uint32_t len, +uint16_t nvme_bounce_data(NvmeCtrl *n, void *ptr, uint32_t len, NvmeTxDirection dir, NvmeRequest *req); -uint16_t nvme_bounce_mdata(NvmeCtrl *n, uint8_t *ptr, uint32_t len, +uint16_t nvme_bounce_mdata(NvmeCtrl *n, void *ptr, uint32_t len, NvmeTxDirection dir, NvmeRequest *req); void nvme_rw_complete_cb(void *opaque, int ret); uint16_t nvme_map_dptr(NvmeCtrl *n, NvmeSg *sg, size_t len, From 6190d92ff70c177e901a85fe0c2da44e34c606f9 Mon Sep 17 00:00:00 2001 From: Klaus Jensen Date: Mon, 22 Nov 2021 23:22:27 +0100 Subject: [PATCH 420/460] hw/nvme: add struct for zone management send Add struct for Zone Management Send in preparation for more zone send flags. Reviewed-by: Keith Busch Signed-off-by: Klaus Jensen --- hw/nvme/ctrl.c | 10 ++++------ include/block/nvme.h | 19 +++++++++++++++++++ 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c index 4344405e59..7cb4974c5e 100644 --- a/hw/nvme/ctrl.c +++ b/hw/nvme/ctrl.c @@ -3616,26 +3616,24 @@ done: static uint16_t nvme_zone_mgmt_send(NvmeCtrl *n, NvmeRequest *req) { - NvmeCmd *cmd = (NvmeCmd *)&req->cmd; + NvmeZoneSendCmd *cmd = (NvmeZoneSendCmd *)&req->cmd; NvmeNamespace *ns = req->ns; NvmeZone *zone; NvmeZoneResetAIOCB *iocb; uint8_t *zd_ext; - uint32_t dw13 = le32_to_cpu(cmd->cdw13); uint64_t slba = 0; uint32_t zone_idx = 0; uint16_t status; - uint8_t action; + uint8_t action = cmd->zsa; bool all; enum NvmeZoneProcessingMask proc_mask = NVME_PROC_CURRENT_ZONE; - action = dw13 & 0xff; - all = !!(dw13 & 0x100); + all = cmd->zsflags & NVME_ZSFLAG_SELECT_ALL; req->status = NVME_SUCCESS; if (!all) { - status = nvme_get_mgmt_zone_slba_idx(ns, cmd, &slba, &zone_idx); + status = nvme_get_mgmt_zone_slba_idx(ns, &req->cmd, &slba, &zone_idx); if (status) { return status; } diff --git a/include/block/nvme.h b/include/block/nvme.h index e3bd47bf76..709d491c70 100644 --- a/include/block/nvme.h +++ b/include/block/nvme.h @@ -1433,6 +1433,21 @@ enum NvmeZoneType { NVME_ZONE_TYPE_SEQ_WRITE = 0x02, }; +typedef struct QEMU_PACKED NvmeZoneSendCmd { + uint8_t opcode; + uint8_t flags; + uint16_t cid; + uint32_t nsid; + uint32_t rsvd8[4]; + NvmeCmdDptr dptr; + uint64_t slba; + uint32_t rsvd48; + uint8_t zsa; + uint8_t zsflags; + uint8_t rsvd54[2]; + uint32_t rsvd56[2]; +} NvmeZoneSendCmd; + enum NvmeZoneSendAction { NVME_ZONE_ACTION_RSD = 0x00, NVME_ZONE_ACTION_CLOSE = 0x01, @@ -1443,6 +1458,10 @@ enum NvmeZoneSendAction { NVME_ZONE_ACTION_SET_ZD_EXT = 0x10, }; +enum { + NVME_ZSFLAG_SELECT_ALL = 1 << 0, +}; + typedef struct QEMU_PACKED NvmeZoneDescr { uint8_t zt; uint8_t zs; From 25872031e14edf6a47bff1c015a026afe5c1c967 Mon Sep 17 00:00:00 2001 From: Klaus Jensen Date: Mon, 22 Nov 2021 23:38:31 +0100 Subject: [PATCH 421/460] hw/nvme: add ozcs enum Add enumeration for OZCS values. Reviewed-by: Keith Busch Signed-off-by: Klaus Jensen --- hw/nvme/ns.c | 3 ++- include/block/nvme.h | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/hw/nvme/ns.c b/hw/nvme/ns.c index 8b5f98c761..356b6c1c2f 100644 --- a/hw/nvme/ns.c +++ b/hw/nvme/ns.c @@ -266,7 +266,8 @@ static void nvme_ns_init_zoned(NvmeNamespace *ns) id_ns_z->mar = cpu_to_le32(ns->params.max_active_zones - 1); id_ns_z->mor = cpu_to_le32(ns->params.max_open_zones - 1); id_ns_z->zoc = 0; - id_ns_z->ozcs = ns->params.cross_zone_read ? 0x01 : 0x00; + id_ns_z->ozcs = ns->params.cross_zone_read ? + NVME_ID_NS_ZONED_OZCS_RAZB : 0x00; for (i = 0; i <= ns->id_ns.nlbaf; i++) { id_ns_z->lbafe[i].zsze = cpu_to_le64(ns->zone_size); diff --git a/include/block/nvme.h b/include/block/nvme.h index 709d491c70..e10ea6f0eb 100644 --- a/include/block/nvme.h +++ b/include/block/nvme.h @@ -1351,6 +1351,10 @@ typedef struct QEMU_PACKED NvmeIdNsZoned { uint8_t vs[256]; } NvmeIdNsZoned; +enum NvmeIdNsZonedOzcs { + NVME_ID_NS_ZONED_OZCS_RAZB = 1 << 0, +}; + /*Deallocate Logical Block Features*/ #define NVME_ID_NS_DLFEAT_GUARD_CRC(dlfeat) ((dlfeat) & 0x10) #define NVME_ID_NS_DLFEAT_WRITE_ZEROES(dlfeat) ((dlfeat) & 0x08) From e321b4cdc2dd0b5e806ecf759138be7f83774142 Mon Sep 17 00:00:00 2001 From: Klaus Jensen Date: Thu, 4 Mar 2021 08:40:11 +0100 Subject: [PATCH 422/460] hw/nvme: add support for zoned random write area Add support for TP 4076 ("Zoned Random Write Area"), v2021.08.23 ("Ratified"). This adds three new namespace parameters: "zoned.numzrwa" (number of zrwa resources, i.e. number of zones that can have a zrwa), "zoned.zrwas" (zrwa size in LBAs), "zoned.zrwafg" (granularity in LBAs for flushes). Reviewed-by: Keith Busch Signed-off-by: Klaus Jensen --- hw/nvme/ctrl.c | 171 ++++++++++++++++++++++++++++++++++++++----- hw/nvme/ns.c | 58 +++++++++++++++ hw/nvme/nvme.h | 10 +++ hw/nvme/trace-events | 1 + include/block/nvme.h | 17 ++++- 5 files changed, 237 insertions(+), 20 deletions(-) diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c index 7cb4974c5e..98aac98bef 100644 --- a/hw/nvme/ctrl.c +++ b/hw/nvme/ctrl.c @@ -299,26 +299,37 @@ static void nvme_assign_zone_state(NvmeNamespace *ns, NvmeZone *zone, } } -/* - * Check if we can open a zone without exceeding open/active limits. - * AOR stands for "Active and Open Resources" (see TP 4053 section 2.5). - */ -static int nvme_aor_check(NvmeNamespace *ns, uint32_t act, uint32_t opn) +static uint16_t nvme_zns_check_resources(NvmeNamespace *ns, uint32_t act, + uint32_t opn, uint32_t zrwa) { if (ns->params.max_active_zones != 0 && ns->nr_active_zones + act > ns->params.max_active_zones) { trace_pci_nvme_err_insuff_active_res(ns->params.max_active_zones); return NVME_ZONE_TOO_MANY_ACTIVE | NVME_DNR; } + if (ns->params.max_open_zones != 0 && ns->nr_open_zones + opn > ns->params.max_open_zones) { trace_pci_nvme_err_insuff_open_res(ns->params.max_open_zones); return NVME_ZONE_TOO_MANY_OPEN | NVME_DNR; } + if (zrwa > ns->zns.numzrwa) { + return NVME_NOZRWA | NVME_DNR; + } + return NVME_SUCCESS; } +/* + * Check if we can open a zone without exceeding open/active limits. + * AOR stands for "Active and Open Resources" (see TP 4053 section 2.5). + */ +static uint16_t nvme_aor_check(NvmeNamespace *ns, uint32_t act, uint32_t opn) +{ + return nvme_zns_check_resources(ns, act, opn, 0); +} + static bool nvme_addr_is_cmb(NvmeCtrl *n, hwaddr addr) { hwaddr hi, lo; @@ -1628,9 +1639,19 @@ static uint16_t nvme_check_zone_write(NvmeNamespace *ns, NvmeZone *zone, return status; } - if (unlikely(slba != zone->w_ptr)) { - trace_pci_nvme_err_write_not_at_wp(slba, zone->d.zslba, zone->w_ptr); - return NVME_ZONE_INVALID_WRITE; + if (zone->d.za & NVME_ZA_ZRWA_VALID) { + uint64_t ezrwa = zone->w_ptr + 2 * ns->zns.zrwas; + + if (slba < zone->w_ptr || slba + nlb > ezrwa) { + trace_pci_nvme_err_zone_invalid_write(slba, zone->w_ptr); + return NVME_ZONE_INVALID_WRITE; + } + } else { + if (unlikely(slba != zone->w_ptr)) { + trace_pci_nvme_err_write_not_at_wp(slba, zone->d.zslba, + zone->w_ptr); + return NVME_ZONE_INVALID_WRITE; + } } if (unlikely((slba + nlb) > zcap)) { @@ -1710,6 +1731,14 @@ static uint16_t nvme_zrm_finish(NvmeNamespace *ns, NvmeZone *zone) /* fallthrough */ case NVME_ZONE_STATE_CLOSED: nvme_aor_dec_active(ns); + + if (zone->d.za & NVME_ZA_ZRWA_VALID) { + zone->d.za &= ~NVME_ZA_ZRWA_VALID; + if (ns->params.numzrwa) { + ns->zns.numzrwa++; + } + } + /* fallthrough */ case NVME_ZONE_STATE_EMPTY: nvme_assign_zone_state(ns, zone, NVME_ZONE_STATE_FULL); @@ -1745,6 +1774,13 @@ static uint16_t nvme_zrm_reset(NvmeNamespace *ns, NvmeZone *zone) /* fallthrough */ case NVME_ZONE_STATE_CLOSED: nvme_aor_dec_active(ns); + + if (zone->d.za & NVME_ZA_ZRWA_VALID) { + if (ns->params.numzrwa) { + ns->zns.numzrwa++; + } + } + /* fallthrough */ case NVME_ZONE_STATE_FULL: zone->w_ptr = zone->d.zslba; @@ -1778,6 +1814,7 @@ static void nvme_zrm_auto_transition_zone(NvmeNamespace *ns) enum { NVME_ZRM_AUTO = 1 << 0, + NVME_ZRM_ZRWA = 1 << 1, }; static uint16_t nvme_zrm_open_flags(NvmeCtrl *n, NvmeNamespace *ns, @@ -1796,7 +1833,8 @@ static uint16_t nvme_zrm_open_flags(NvmeCtrl *n, NvmeNamespace *ns, if (n->params.auto_transition_zones) { nvme_zrm_auto_transition_zone(ns); } - status = nvme_aor_check(ns, act, 1); + status = nvme_zns_check_resources(ns, act, 1, + (flags & NVME_ZRM_ZRWA) ? 1 : 0); if (status) { return status; } @@ -1824,6 +1862,12 @@ static uint16_t nvme_zrm_open_flags(NvmeCtrl *n, NvmeNamespace *ns, /* fallthrough */ case NVME_ZONE_STATE_EXPLICITLY_OPEN: + if (flags & NVME_ZRM_ZRWA) { + ns->zns.numzrwa--; + + zone->d.za |= NVME_ZA_ZRWA_VALID; + } + return NVME_SUCCESS; default: @@ -1837,12 +1881,6 @@ static inline uint16_t nvme_zrm_auto(NvmeCtrl *n, NvmeNamespace *ns, return nvme_zrm_open_flags(n, ns, zone, NVME_ZRM_AUTO); } -static inline uint16_t nvme_zrm_open(NvmeCtrl *n, NvmeNamespace *ns, - NvmeZone *zone) -{ - return nvme_zrm_open_flags(n, ns, zone, 0); -} - static void nvme_advance_zone_wp(NvmeNamespace *ns, NvmeZone *zone, uint32_t nlb) { @@ -1853,6 +1891,20 @@ static void nvme_advance_zone_wp(NvmeNamespace *ns, NvmeZone *zone, } } +static void nvme_zoned_zrwa_implicit_flush(NvmeNamespace *ns, NvmeZone *zone, + uint32_t nlbc) +{ + uint16_t nzrwafgs = DIV_ROUND_UP(nlbc, ns->zns.zrwafg); + + nlbc = nzrwafgs * ns->zns.zrwafg; + + trace_pci_nvme_zoned_zrwa_implicit_flush(zone->d.zslba, nlbc); + + zone->w_ptr += nlbc; + + nvme_advance_zone_wp(ns, zone, nlbc); +} + static void nvme_finalize_zoned_write(NvmeNamespace *ns, NvmeRequest *req) { NvmeRwCmd *rw = (NvmeRwCmd *)&req->cmd; @@ -1865,6 +1917,17 @@ static void nvme_finalize_zoned_write(NvmeNamespace *ns, NvmeRequest *req) zone = nvme_get_zone_by_slba(ns, slba); assert(zone); + if (zone->d.za & NVME_ZA_ZRWA_VALID) { + uint64_t ezrwa = zone->w_ptr + ns->zns.zrwas - 1; + uint64_t elba = slba + nlb - 1; + + if (elba > ezrwa) { + nvme_zoned_zrwa_implicit_flush(ns, zone, elba - ezrwa); + } + + return; + } + nvme_advance_zone_wp(ns, zone, nlb); } @@ -2665,7 +2728,9 @@ static void nvme_copy_in_completed_cb(void *opaque, int ret) goto invalid; } - iocb->zone->w_ptr += nlb; + if (!(iocb->zone->d.za & NVME_ZA_ZRWA_VALID)) { + iocb->zone->w_ptr += nlb; + } } qemu_iovec_reset(&iocb->iov); @@ -3204,6 +3269,10 @@ static uint16_t nvme_do_write(NvmeCtrl *n, NvmeRequest *req, bool append, if (append) { bool piremap = !!(ctrl & NVME_RW_PIREMAP); + if (unlikely(zone->d.za & NVME_ZA_ZRWA_VALID)) { + return NVME_INVALID_ZONE_OP | NVME_DNR; + } + if (unlikely(slba != zone->d.zslba)) { trace_pci_nvme_err_append_not_at_start(slba, zone->d.zslba); status = NVME_INVALID_FIELD; @@ -3255,7 +3324,9 @@ static uint16_t nvme_do_write(NvmeCtrl *n, NvmeRequest *req, bool append, goto invalid; } - zone->w_ptr += nlb; + if (!(zone->d.za & NVME_ZA_ZRWA_VALID)) { + zone->w_ptr += nlb; + } } data_offset = nvme_l2b(ns, slba); @@ -3339,7 +3410,24 @@ enum NvmeZoneProcessingMask { static uint16_t nvme_open_zone(NvmeNamespace *ns, NvmeZone *zone, NvmeZoneState state, NvmeRequest *req) { - return nvme_zrm_open(nvme_ctrl(req), ns, zone); + NvmeZoneSendCmd *cmd = (NvmeZoneSendCmd *)&req->cmd; + int flags = 0; + + if (cmd->zsflags & NVME_ZSFLAG_ZRWA_ALLOC) { + uint16_t ozcs = le16_to_cpu(ns->id_ns_zoned->ozcs); + + if (!(ozcs & NVME_ID_NS_ZONED_OZCS_ZRWASUP)) { + return NVME_INVALID_ZONE_OP | NVME_DNR; + } + + if (zone->w_ptr % ns->zns.zrwafg) { + return NVME_NOZRWA | NVME_DNR; + } + + flags = NVME_ZRM_ZRWA; + } + + return nvme_zrm_open_flags(nvme_ctrl(req), ns, zone, flags); } static uint16_t nvme_close_zone(NvmeNamespace *ns, NvmeZone *zone, @@ -3614,6 +3702,44 @@ done: } } +static uint16_t nvme_zone_mgmt_send_zrwa_flush(NvmeCtrl *n, NvmeZone *zone, + uint64_t elba, NvmeRequest *req) +{ + NvmeNamespace *ns = req->ns; + uint16_t ozcs = le16_to_cpu(ns->id_ns_zoned->ozcs); + uint64_t wp = zone->d.wp; + uint32_t nlb = elba - wp + 1; + uint16_t status; + + + if (!(ozcs & NVME_ID_NS_ZONED_OZCS_ZRWASUP)) { + return NVME_INVALID_ZONE_OP | NVME_DNR; + } + + if (!(zone->d.za & NVME_ZA_ZRWA_VALID)) { + return NVME_INVALID_FIELD | NVME_DNR; + } + + if (elba < wp || elba > wp + ns->zns.zrwas) { + return NVME_ZONE_BOUNDARY_ERROR | NVME_DNR; + } + + if (nlb % ns->zns.zrwafg) { + return NVME_INVALID_FIELD | NVME_DNR; + } + + status = nvme_zrm_auto(n, ns, zone); + if (status) { + return status; + } + + zone->w_ptr += nlb; + + nvme_advance_zone_wp(ns, zone, nlb); + + return NVME_SUCCESS; +} + static uint16_t nvme_zone_mgmt_send(NvmeCtrl *n, NvmeRequest *req) { NvmeZoneSendCmd *cmd = (NvmeZoneSendCmd *)&req->cmd; @@ -3640,7 +3766,7 @@ static uint16_t nvme_zone_mgmt_send(NvmeCtrl *n, NvmeRequest *req) } zone = &ns->zone_array[zone_idx]; - if (slba != zone->d.zslba) { + if (slba != zone->d.zslba && action != NVME_ZONE_ACTION_ZRWA_FLUSH) { trace_pci_nvme_err_unaligned_zone_cmd(action, slba, zone->d.zslba); return NVME_INVALID_FIELD | NVME_DNR; } @@ -3716,6 +3842,13 @@ static uint16_t nvme_zone_mgmt_send(NvmeCtrl *n, NvmeRequest *req) } break; + case NVME_ZONE_ACTION_ZRWA_FLUSH: + if (all) { + return NVME_INVALID_FIELD | NVME_DNR; + } + + return nvme_zone_mgmt_send_zrwa_flush(n, zone, slba, req); + default: trace_pci_nvme_err_invalid_mgmt_action(action); status = NVME_INVALID_FIELD; diff --git a/hw/nvme/ns.c b/hw/nvme/ns.c index 356b6c1c2f..ee673f1a5b 100644 --- a/hw/nvme/ns.c +++ b/hw/nvme/ns.c @@ -275,6 +275,23 @@ static void nvme_ns_init_zoned(NvmeNamespace *ns) ns->params.zd_extension_size >> 6; /* Units of 64B */ } + if (ns->params.zrwas) { + ns->zns.numzrwa = ns->params.numzrwa ? + ns->params.numzrwa : ns->num_zones; + + ns->zns.zrwas = ns->params.zrwas >> ns->lbaf.ds; + ns->zns.zrwafg = ns->params.zrwafg >> ns->lbaf.ds; + + id_ns_z->ozcs |= NVME_ID_NS_ZONED_OZCS_ZRWASUP; + id_ns_z->zrwacap = NVME_ID_NS_ZONED_ZRWACAP_EXPFLUSHSUP; + + id_ns_z->numzrwa = cpu_to_le32(ns->params.numzrwa); + id_ns_z->zrwas = cpu_to_le16(ns->zns.zrwas); + id_ns_z->zrwafg = cpu_to_le16(ns->zns.zrwafg); + } + + id_ns_z->ozcs = cpu_to_le16(id_ns_z->ozcs); + ns->csi = NVME_CSI_ZONED; ns->id_ns.nsze = cpu_to_le64(ns->num_zones * ns->zone_size); ns->id_ns.ncap = ns->id_ns.nsze; @@ -315,6 +332,10 @@ static void nvme_clear_zone(NvmeNamespace *ns, NvmeZone *zone) QTAILQ_INSERT_HEAD(&ns->closed_zones, zone, entry); } else { trace_pci_nvme_clear_ns_reset(state, zone->d.zslba); + if (zone->d.za & NVME_ZA_ZRWA_VALID) { + zone->d.za &= ~NVME_ZA_ZRWA_VALID; + ns->zns.numzrwa++; + } nvme_set_zone_state(zone, NVME_ZONE_STATE_EMPTY); } } @@ -392,6 +413,40 @@ static int nvme_ns_check_constraints(NvmeNamespace *ns, Error **errp) return -1; } } + + if (ns->params.zrwas) { + if (ns->params.zrwas % ns->blkconf.logical_block_size) { + error_setg(errp, "zone random write area size (zoned.zrwas " + "%"PRIu64") must be a multiple of the logical " + "block size (logical_block_size %"PRIu32")", + ns->params.zrwas, ns->blkconf.logical_block_size); + return -1; + } + + if (ns->params.zrwafg == -1) { + ns->params.zrwafg = ns->blkconf.logical_block_size; + } + + if (ns->params.zrwas % ns->params.zrwafg) { + error_setg(errp, "zone random write area size (zoned.zrwas " + "%"PRIu64") must be a multiple of the zone random " + "write area flush granularity (zoned.zrwafg, " + "%"PRIu64")", ns->params.zrwas, ns->params.zrwafg); + return -1; + } + + if (ns->params.max_active_zones) { + if (ns->params.numzrwa > ns->params.max_active_zones) { + error_setg(errp, "number of zone random write area " + "resources (zoned.numzrwa, %d) must be less " + "than or equal to maximum active resources " + "(zoned.max_active_zones, %d)", + ns->params.numzrwa, + ns->params.max_active_zones); + return -1; + } + } + } } return 0; @@ -551,6 +606,9 @@ static Property nvme_ns_props[] = { params.max_open_zones, 0), DEFINE_PROP_UINT32("zoned.descr_ext_size", NvmeNamespace, params.zd_extension_size, 0), + DEFINE_PROP_UINT32("zoned.numzrwa", NvmeNamespace, params.numzrwa, 0), + DEFINE_PROP_SIZE("zoned.zrwas", NvmeNamespace, params.zrwas, 0), + DEFINE_PROP_SIZE("zoned.zrwafg", NvmeNamespace, params.zrwafg, -1), DEFINE_PROP_BOOL("eui64-default", NvmeNamespace, params.eui64_default, true), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/nvme/nvme.h b/hw/nvme/nvme.h index 38f3ebf7f6..90c0bb7ce2 100644 --- a/hw/nvme/nvme.h +++ b/hw/nvme/nvme.h @@ -114,6 +114,10 @@ typedef struct NvmeNamespaceParams { uint32_t max_active_zones; uint32_t max_open_zones; uint32_t zd_extension_size; + + uint32_t numzrwa; + uint64_t zrwas; + uint64_t zrwafg; } NvmeNamespaceParams; typedef struct NvmeNamespace { @@ -130,6 +134,12 @@ typedef struct NvmeNamespace { uint16_t status; int attached; + struct { + uint16_t zrwas; + uint16_t zrwafg; + uint32_t numzrwa; + } zns; + QTAILQ_ENTRY(NvmeNamespace) entry; NvmeIdNsZoned *id_ns_zoned; diff --git a/hw/nvme/trace-events b/hw/nvme/trace-events index ff6cafd520..90730d802f 100644 --- a/hw/nvme/trace-events +++ b/hw/nvme/trace-events @@ -103,6 +103,7 @@ pci_nvme_set_descriptor_extension(uint64_t slba, uint32_t zone_idx) "set zone de pci_nvme_zd_extension_set(uint32_t zone_idx) "set descriptor extension for zone_idx=%"PRIu32"" pci_nvme_clear_ns_close(uint32_t state, uint64_t slba) "zone state=%"PRIu32", slba=%"PRIu64" transitioned to Closed state" pci_nvme_clear_ns_reset(uint32_t state, uint64_t slba) "zone state=%"PRIu32", slba=%"PRIu64" transitioned to Empty state" +pci_nvme_zoned_zrwa_implicit_flush(uint64_t zslba, uint32_t nlb) "zslba 0x%"PRIx64" nlb %"PRIu32"" # error conditions pci_nvme_err_mdts(size_t len) "len %zu" diff --git a/include/block/nvme.h b/include/block/nvme.h index e10ea6f0eb..cd068ac891 100644 --- a/include/block/nvme.h +++ b/include/block/nvme.h @@ -890,6 +890,8 @@ enum NvmeStatusCodes { NVME_INVALID_PROT_INFO = 0x0181, NVME_WRITE_TO_RO = 0x0182, NVME_CMD_SIZE_LIMIT = 0x0183, + NVME_INVALID_ZONE_OP = 0x01b6, + NVME_NOZRWA = 0x01b7, NVME_ZONE_BOUNDARY_ERROR = 0x01b8, NVME_ZONE_FULL = 0x01b9, NVME_ZONE_READ_ONLY = 0x01ba, @@ -1345,7 +1347,12 @@ typedef struct QEMU_PACKED NvmeIdNsZoned { uint32_t mor; uint32_t rrl; uint32_t frl; - uint8_t rsvd20[2796]; + uint8_t rsvd12[24]; + uint32_t numzrwa; + uint16_t zrwafg; + uint16_t zrwas; + uint8_t zrwacap; + uint8_t rsvd53[2763]; NvmeLBAFE lbafe[16]; uint8_t rsvd3072[768]; uint8_t vs[256]; @@ -1353,6 +1360,11 @@ typedef struct QEMU_PACKED NvmeIdNsZoned { enum NvmeIdNsZonedOzcs { NVME_ID_NS_ZONED_OZCS_RAZB = 1 << 0, + NVME_ID_NS_ZONED_OZCS_ZRWASUP = 1 << 1, +}; + +enum NvmeIdNsZonedZrwacap { + NVME_ID_NS_ZONED_ZRWACAP_EXPFLUSHSUP = 1 << 0, }; /*Deallocate Logical Block Features*/ @@ -1408,6 +1420,7 @@ enum NvmeZoneAttr { NVME_ZA_FINISHED_BY_CTLR = 1 << 0, NVME_ZA_FINISH_RECOMMENDED = 1 << 1, NVME_ZA_RESET_RECOMMENDED = 1 << 2, + NVME_ZA_ZRWA_VALID = 1 << 3, NVME_ZA_ZD_EXT_VALID = 1 << 7, }; @@ -1460,10 +1473,12 @@ enum NvmeZoneSendAction { NVME_ZONE_ACTION_RESET = 0x04, NVME_ZONE_ACTION_OFFLINE = 0x05, NVME_ZONE_ACTION_SET_ZD_EXT = 0x10, + NVME_ZONE_ACTION_ZRWA_FLUSH = 0x11, }; enum { NVME_ZSFLAG_SELECT_ALL = 1 << 0, + NVME_ZSFLAG_ZRWA_ALLOC = 1 << 1, }; typedef struct QEMU_PACKED NvmeZoneDescr { From 7b223e38603de3a75602e14914d26f9d4baf52eb Mon Sep 17 00:00:00 2001 From: Christian Ehrhardt Date: Wed, 9 Feb 2022 12:14:56 +0100 Subject: [PATCH 423/460] tools/virtiofsd: Add rseq syscall to the seccomp allowlist The virtiofsd currently crashes when used with glibc 2.35. That is due to the rseq system call being added to every thread creation [1][2]. [1]: https://www.efficios.com/blog/2019/02/08/linux-restartable-sequences/ [2]: https://sourceware.org/pipermail/libc-alpha/2022-February/136040.html This happens not at daemon start, but when a guest connects /usr/lib/qemu/virtiofsd -f --socket-path=/tmp/testvfsd -o sandbox=chroot \ -o source=/var/guests/j-virtiofs --socket-group=kvm virtio_session_mount: Waiting for vhost-user socket connection... # start ok, now guest will connect virtio_session_mount: Received vhost-user socket connection virtio_loop: Entry fv_queue_set_started: qidx=0 started=1 fv_queue_set_started: qidx=1 started=1 Bad system call (core dumped) We have to put rseq on the seccomp allowlist to avoid that the daemon is crashing in this case. Reported-by: Michael Hudson-Doyle Signed-off-by: Christian Ehrhardt Reviewed-by: Dr. David Alan Gilbert Message-id: 20220209111456.3328420-1-christian.ehrhardt@canonical.com [Moved rseq to its alphabetically ordered position in the seccomp allowlist. --Stefan] Signed-off-by: Stefan Hajnoczi --- tools/virtiofsd/passthrough_seccomp.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tools/virtiofsd/passthrough_seccomp.c b/tools/virtiofsd/passthrough_seccomp.c index a3ce9f898d..2bc0127b69 100644 --- a/tools/virtiofsd/passthrough_seccomp.c +++ b/tools/virtiofsd/passthrough_seccomp.c @@ -91,6 +91,9 @@ static const int syscall_allowlist[] = { SCMP_SYS(renameat2), SCMP_SYS(removexattr), SCMP_SYS(restart_syscall), +#ifdef __NR_rseq + SCMP_SYS(rseq), /* required since glibc 2.35 */ +#endif SCMP_SYS(rt_sigaction), SCMP_SYS(rt_sigprocmask), SCMP_SYS(rt_sigreturn), From 34deee7b6a1418f3d62a91ff0a9d156e60a788a5 Mon Sep 17 00:00:00 2001 From: "Dr. David Alan Gilbert" Date: Thu, 10 Feb 2022 17:47:14 +0000 Subject: [PATCH 424/460] Deprecate C virtiofsd MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There's a nice new Rust implementation out there; recommend people do new work on that. Signed-off-by: Dr. David Alan Gilbert Reviewed-by: Daniel P. Berrangé Message-id: 20220210174714.19843-1-dgilbert@redhat.com Signed-off-by: Stefan Hajnoczi --- docs/about/deprecated.rst | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index 25b1fb8677..26d00812ba 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -447,3 +447,20 @@ nanoMIPS ISA The ``nanoMIPS`` ISA has never been upstreamed to any compiler toolchain. As it is hard to generate binaries for it, declare it deprecated. + +Tools +----- + +virtiofsd +''''''''' + +There is a new Rust implementation of ``virtiofsd`` at +``https://gitlab.com/virtio-fs/virtiofsd``; +since this is now marked stable, new development should be done on that +rather than the existing C version in the QEMU tree. +The C version will still accept fixes and patches that +are already in development for the moment, but will eventually +be deleted from this tree. +New deployments should use the Rust version, and existing systems +should consider moving to it. The command line and feature set +is very close and moving should be simple. From 4c41c69e05fe28c0f95f8abd2ebf407e95a4f04b Mon Sep 17 00:00:00 2001 From: Hiroki Narukawa Date: Mon, 14 Feb 2022 20:53:02 +0900 Subject: [PATCH 425/460] util: adjust coroutine pool size to virtio block queue Coroutine pool size was 64 from long ago, and the basis was organized in the commit message in 4d68e86b. At that time, virtio-blk queue-size and num-queue were not configuable, and equivalent values were 128 and 1. Coroutine pool size 64 was fine then. Later queue-size and num-queue got configuable, and default values were increased. Coroutine pool with size 64 exhausts frequently with random disk IO in new size, and slows down. This commit adjusts coroutine pool size adaptively with new values. This commit adds 64 by default, but now coroutine is not only for block devices, and is not too much burdon comparing with new default. pool size of 128 * vCPUs. Signed-off-by: Hiroki Narukawa Message-id: 20220214115302.13294-2-hnarukaw@yahoo-corp.jp Signed-off-by: Stefan Hajnoczi --- hw/block/virtio-blk.c | 5 +++++ include/qemu/coroutine.h | 10 ++++++++++ util/qemu-coroutine.c | 20 ++++++++++++++++---- 3 files changed, 31 insertions(+), 4 deletions(-) diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c index 82676cdd01..540c38f829 100644 --- a/hw/block/virtio-blk.c +++ b/hw/block/virtio-blk.c @@ -32,6 +32,7 @@ #include "hw/virtio/virtio-bus.h" #include "migration/qemu-file-types.h" #include "hw/virtio/virtio-access.h" +#include "qemu/coroutine.h" /* Config size before the discard support (hide associated config fields) */ #define VIRTIO_BLK_CFG_SIZE offsetof(struct virtio_blk_config, \ @@ -1214,6 +1215,8 @@ static void virtio_blk_device_realize(DeviceState *dev, Error **errp) for (i = 0; i < conf->num_queues; i++) { virtio_add_queue(vdev, conf->queue_size, virtio_blk_handle_output); } + qemu_coroutine_increase_pool_batch_size(conf->num_queues * conf->queue_size + / 2); virtio_blk_data_plane_create(vdev, conf, &s->dataplane, &err); if (err != NULL) { error_propagate(errp, err); @@ -1250,6 +1253,8 @@ static void virtio_blk_device_unrealize(DeviceState *dev) for (i = 0; i < conf->num_queues; i++) { virtio_del_queue(vdev, i); } + qemu_coroutine_decrease_pool_batch_size(conf->num_queues * conf->queue_size + / 2); qemu_del_vm_change_state_handler(s->change); blockdev_mark_auto_del(s->blk); virtio_cleanup(vdev); diff --git a/include/qemu/coroutine.h b/include/qemu/coroutine.h index 4829ff373d..c828a95ee0 100644 --- a/include/qemu/coroutine.h +++ b/include/qemu/coroutine.h @@ -331,6 +331,16 @@ void qemu_co_sleep_wake(QemuCoSleep *w); */ void coroutine_fn yield_until_fd_readable(int fd); +/** + * Increase coroutine pool size + */ +void qemu_coroutine_increase_pool_batch_size(unsigned int additional_pool_size); + +/** + * Devcrease coroutine pool size + */ +void qemu_coroutine_decrease_pool_batch_size(unsigned int additional_pool_size); + #include "qemu/lockable.h" #endif /* QEMU_COROUTINE_H */ diff --git a/util/qemu-coroutine.c b/util/qemu-coroutine.c index 38fb6d3084..c03b2422ff 100644 --- a/util/qemu-coroutine.c +++ b/util/qemu-coroutine.c @@ -20,12 +20,14 @@ #include "qemu/coroutine_int.h" #include "block/aio.h" +/** Initial batch size is 64, and is increased on demand */ enum { - POOL_BATCH_SIZE = 64, + POOL_INITIAL_BATCH_SIZE = 64, }; /** Free list to speed up creation */ static QSLIST_HEAD(, Coroutine) release_pool = QSLIST_HEAD_INITIALIZER(pool); +static unsigned int pool_batch_size = POOL_INITIAL_BATCH_SIZE; static unsigned int release_pool_size; static __thread QSLIST_HEAD(, Coroutine) alloc_pool = QSLIST_HEAD_INITIALIZER(pool); static __thread unsigned int alloc_pool_size; @@ -49,7 +51,7 @@ Coroutine *qemu_coroutine_create(CoroutineEntry *entry, void *opaque) if (CONFIG_COROUTINE_POOL) { co = QSLIST_FIRST(&alloc_pool); if (!co) { - if (release_pool_size > POOL_BATCH_SIZE) { + if (release_pool_size > qatomic_read(&pool_batch_size)) { /* Slow path; a good place to register the destructor, too. */ if (!coroutine_pool_cleanup_notifier.notify) { coroutine_pool_cleanup_notifier.notify = coroutine_pool_cleanup; @@ -86,12 +88,12 @@ static void coroutine_delete(Coroutine *co) co->caller = NULL; if (CONFIG_COROUTINE_POOL) { - if (release_pool_size < POOL_BATCH_SIZE * 2) { + if (release_pool_size < qatomic_read(&pool_batch_size) * 2) { QSLIST_INSERT_HEAD_ATOMIC(&release_pool, co, pool_next); qatomic_inc(&release_pool_size); return; } - if (alloc_pool_size < POOL_BATCH_SIZE) { + if (alloc_pool_size < qatomic_read(&pool_batch_size)) { QSLIST_INSERT_HEAD(&alloc_pool, co, pool_next); alloc_pool_size++; return; @@ -202,3 +204,13 @@ AioContext *coroutine_fn qemu_coroutine_get_aio_context(Coroutine *co) { return co->ctx; } + +void qemu_coroutine_increase_pool_batch_size(unsigned int additional_pool_size) +{ + qatomic_add(&pool_batch_size, additional_pool_size); +} + +void qemu_coroutine_decrease_pool_batch_size(unsigned int removing_pool_size) +{ + qatomic_sub(&pool_batch_size, removing_pool_size); +} From a6b7bd35f307bafb1f05d248194ae976e29949c8 Mon Sep 17 00:00:00 2001 From: Wilfred Mallawa Date: Fri, 21 Jan 2022 15:50:05 +1000 Subject: [PATCH 426/460] include: hw: remove ibex_plic.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch removes the left-over/unused `ibex_plic.h` file. Previously used by opentitan, which now follows the RISC-V standard and uses the SiFivePlicState. Fixes: 434e7e021 ("hw/intc: Remove the Ibex PLIC") Signed-off-by: Wilfred Mallawa Reviewed-by: Alistair Francis Reviewed-by: Philippe Mathieu-Daudé Message-id: 20220121055005.3159846-1-alistair.francis@opensource.wdc.com Signed-off-by: Alistair Francis --- include/hw/intc/ibex_plic.h | 67 ------------------------------------- 1 file changed, 67 deletions(-) delete mode 100644 include/hw/intc/ibex_plic.h diff --git a/include/hw/intc/ibex_plic.h b/include/hw/intc/ibex_plic.h deleted file mode 100644 index d596436e06..0000000000 --- a/include/hw/intc/ibex_plic.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * QEMU RISC-V lowRISC Ibex PLIC - * - * Copyright (c) 2020 Western Digital - * - * 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 . - */ - -#ifndef HW_IBEX_PLIC_H -#define HW_IBEX_PLIC_H - -#include "hw/sysbus.h" -#include "qom/object.h" - -#define TYPE_IBEX_PLIC "ibex-plic" -OBJECT_DECLARE_SIMPLE_TYPE(IbexPlicState, IBEX_PLIC) - -struct IbexPlicState { - /*< private >*/ - SysBusDevice parent_obj; - - /*< public >*/ - MemoryRegion mmio; - - uint32_t *pending; - uint32_t *hidden_pending; - uint32_t *claimed; - uint32_t *source; - uint32_t *priority; - uint32_t *enable; - uint32_t threshold; - uint32_t claim; - - /* config */ - uint32_t num_cpus; - uint32_t num_sources; - - uint32_t pending_base; - uint32_t pending_num; - - uint32_t source_base; - uint32_t source_num; - - uint32_t priority_base; - uint32_t priority_num; - - uint32_t enable_base; - uint32_t enable_num; - - uint32_t threshold_base; - - uint32_t claim_base; - - qemu_irq *external_irqs; -}; - -#endif /* HW_IBEX_PLIC_H */ From f42483d776bce29a9925ed61cc10eb27a5b2446c Mon Sep 17 00:00:00 2001 From: Petr Tesarik Date: Thu, 20 Jan 2022 10:27:15 +0100 Subject: [PATCH 427/460] Allow setting up to 8 bytes with the generic loader MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The documentation for the generic loader says that "the maximum size of the data is 8 bytes". However, attempts to set data-len=8 trigger the following assertion failure: ../hw/core/generic-loader.c:59: generic_loader_reset: Assertion `s->data_len < sizeof(s->data)' failed. The type of s->data is uint64_t (i.e. 8 bytes long), so I believe this assert should use <= instead of <. Fixes: e481a1f63c93 ("generic-loader: Add a generic loader") Signed-off-by: Petr Tesarik Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Alistair Francis Message-id: 20220120092715.7805-1-ptesarik@suse.com Signed-off-by: Alistair Francis --- hw/core/generic-loader.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/core/generic-loader.c b/hw/core/generic-loader.c index 9a24ffb880..504ed7ca72 100644 --- a/hw/core/generic-loader.c +++ b/hw/core/generic-loader.c @@ -56,7 +56,7 @@ static void generic_loader_reset(void *opaque) } if (s->data_len) { - assert(s->data_len < sizeof(s->data)); + assert(s->data_len <= sizeof(s->data)); dma_memory_write(s->cpu->as, s->addr, &s->data, s->data_len, MEMTXATTRS_UNSPECIFIED); } From 6c3a9247259940069402ee169e63aac0ac5f8f6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20P=C3=A9trot?= Date: Mon, 24 Jan 2022 21:24:56 +0100 Subject: [PATCH 428/460] target/riscv: correct "code should not be reached" for x-rv128 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The addition of uxl support in gdbstub adds a few checks on the maximum register length, but omitted MXL_RV128, an experimental feature. This patch makes rv128 react as rv64, as previously. Signed-off-by: Frédéric Pétrot Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: LIU Zhiwei Reviewed-by: Alistair Francis Message-id: 20220124202456.420258-1-frederic.petrot@univ-grenoble-alpes.fr Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 3 +-- target/riscv/gdbstub.c | 3 +++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 1cb0436187..5ada71e5bf 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -528,9 +528,8 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp) switch (env->misa_mxl_max) { #ifdef TARGET_RISCV64 case MXL_RV64: - cc->gdb_core_xml_file = "riscv-64bit-cpu.xml"; - break; case MXL_RV128: + cc->gdb_core_xml_file = "riscv-64bit-cpu.xml"; break; #endif case MXL_RV32: diff --git a/target/riscv/gdbstub.c b/target/riscv/gdbstub.c index f531a74c2f..9ed049c29e 100644 --- a/target/riscv/gdbstub.c +++ b/target/riscv/gdbstub.c @@ -64,6 +64,7 @@ int riscv_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) case MXL_RV32: return gdb_get_reg32(mem_buf, tmp); case MXL_RV64: + case MXL_RV128: return gdb_get_reg64(mem_buf, tmp); default: g_assert_not_reached(); @@ -84,6 +85,7 @@ int riscv_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) length = 4; break; case MXL_RV64: + case MXL_RV128: if (env->xl < MXL_RV64) { tmp = (int32_t)ldq_p(mem_buf); } else { @@ -420,6 +422,7 @@ void riscv_cpu_register_gdb_regs_for_features(CPUState *cs) 1, "riscv-32bit-virtual.xml", 0); break; case MXL_RV64: + case MXL_RV128: gdb_register_coprocessor(cs, riscv_gdb_get_virtual, riscv_gdb_set_virtual, 1, "riscv-64bit-virtual.xml", 0); From 466292bd4aa0908794116d96dad816e4155e8b69 Mon Sep 17 00:00:00 2001 From: Philipp Tomsich Date: Wed, 2 Feb 2022 01:52:43 +0100 Subject: [PATCH 429/460] target/riscv: refactor (anonymous struct) RISCVCPU.cfg into 'struct RISCVCPUConfig' Signed-off-by: Philipp Tomsich Reviewed-by: Alistair Francis Suggested-by: Richard Henderson Reviewed-by: Richard Henderson Message-Id: <20220202005249.3566542-2-philipp.tomsich@vrull.eu> Signed-off-by: Alistair Francis --- target/riscv/cpu.h | 78 ++++++++++++++++++++++++---------------------- 1 file changed, 41 insertions(+), 37 deletions(-) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 55635d68d5..1175915c0d 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -303,6 +303,46 @@ struct RISCVCPUClass { DeviceReset parent_reset; }; +struct RISCVCPUConfig { + bool ext_i; + bool ext_e; + bool ext_g; + bool ext_m; + bool ext_a; + bool ext_f; + bool ext_d; + bool ext_c; + bool ext_s; + bool ext_u; + bool ext_h; + bool ext_j; + bool ext_v; + bool ext_zba; + bool ext_zbb; + bool ext_zbc; + bool ext_zbs; + bool ext_counters; + bool ext_ifencei; + bool ext_icsr; + bool ext_zfh; + bool ext_zfhmin; + bool ext_zve32f; + bool ext_zve64f; + + char *priv_spec; + char *user_spec; + char *bext_spec; + char *vext_spec; + uint16_t vlen; + uint16_t elen; + bool mmu; + bool pmp; + bool epmp; + uint64_t resetvec; +}; + +typedef struct RISCVCPUConfig RISCVCPUConfig; + /** * RISCVCPU: * @env: #CPURISCVState @@ -320,43 +360,7 @@ struct RISCVCPU { char *dyn_vreg_xml; /* Configuration Settings */ - struct { - bool ext_i; - bool ext_e; - bool ext_g; - bool ext_m; - bool ext_a; - bool ext_f; - bool ext_d; - bool ext_c; - bool ext_s; - bool ext_u; - bool ext_h; - bool ext_j; - bool ext_v; - bool ext_zba; - bool ext_zbb; - bool ext_zbc; - bool ext_zbs; - bool ext_counters; - bool ext_ifencei; - bool ext_icsr; - bool ext_zfh; - bool ext_zfhmin; - bool ext_zve32f; - bool ext_zve64f; - - char *priv_spec; - char *user_spec; - char *bext_spec; - char *vext_spec; - uint16_t vlen; - uint16_t elen; - bool mmu; - bool pmp; - bool epmp; - uint64_t resetvec; - } cfg; + RISCVCPUConfig cfg; }; static inline int riscv_has_ext(CPURISCVState *env, target_ulong ext) From 3b91323e33d85150eb482458b1f1e2b08a59d8a2 Mon Sep 17 00:00:00 2001 From: Philipp Tomsich Date: Wed, 2 Feb 2022 01:52:44 +0100 Subject: [PATCH 430/460] target/riscv: riscv_tr_init_disas_context: copy pointer-to-cfg into cfg_ptr As the number of extensions is growing, copying them individiually into the DisasContext will scale less and less... instead we populate a pointer to the RISCVCPUConfig structure in the DisasContext. This adds an extra indirection when checking for the availability of an extension (compared to copying the fields into DisasContext). While not a performance problem today, we can always (shallow) copy the entire structure into the DisasContext (instead of putting a pointer to it) if this is ever deemed necessary. Signed-off-by: Philipp Tomsich Reviewed-by: Alistair Francis Suggested-by: Richard Henderson Reviewed-by: Richard Henderson Message-Id: <20220202005249.3566542-3-philipp.tomsich@vrull.eu> Signed-off-by: Alistair Francis --- target/riscv/translate.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/target/riscv/translate.c b/target/riscv/translate.c index f0bbe80875..49e40735ce 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -76,6 +76,7 @@ typedef struct DisasContext { int frm; RISCVMXL ol; bool virt_enabled; + const RISCVCPUConfig *cfg_ptr; bool ext_ifencei; bool ext_zfh; bool ext_zfhmin; @@ -908,6 +909,7 @@ static void riscv_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) #endif ctx->misa_ext = env->misa_ext; ctx->frm = -1; /* unknown rounding mode */ + ctx->cfg_ptr = &(cpu->cfg); ctx->ext_ifencei = cpu->cfg.ext_ifencei; ctx->ext_zfh = cpu->cfg.ext_zfh; ctx->ext_zfhmin = cpu->cfg.ext_zfhmin; From 79bf3b51acb4a6245b500005859e8b1d1611302f Mon Sep 17 00:00:00 2001 From: Philipp Tomsich Date: Wed, 2 Feb 2022 01:52:45 +0100 Subject: [PATCH 431/460] target/riscv: access configuration through cfg_ptr in DisasContext The implementation in trans_{rvi,rvv,rvzfh}.c.inc accesses the shallow copies (in DisasContext) of some of the elements available in the RISCVCPUConfig structure. This commit redirects accesses to use the cfg_ptr copied into DisasContext and removes the shallow copies. Signed-off-by: Philipp Tomsich Reviewed-by: Alistair Francis Suggested-by: Richard Henderson Reviewed-by: Richard Henderson Message-Id: <20220202005249.3566542-4-philipp.tomsich@vrull.eu> [ Changes by AF: - Fixup checkpatch failures ] Signed-off-by: Alistair Francis --- target/riscv/insn_trans/trans_rvi.c.inc | 2 +- target/riscv/insn_trans/trans_rvv.c.inc | 146 ++++++++++++++-------- target/riscv/insn_trans/trans_rvzfh.c.inc | 4 +- target/riscv/translate.c | 14 --- 4 files changed, 97 insertions(+), 69 deletions(-) diff --git a/target/riscv/insn_trans/trans_rvi.c.inc b/target/riscv/insn_trans/trans_rvi.c.inc index 3cd1b3f877..f1342f30f8 100644 --- a/target/riscv/insn_trans/trans_rvi.c.inc +++ b/target/riscv/insn_trans/trans_rvi.c.inc @@ -806,7 +806,7 @@ static bool trans_fence(DisasContext *ctx, arg_fence *a) static bool trans_fence_i(DisasContext *ctx, arg_fence_i *a) { - if (!ctx->ext_ifencei) { + if (!ctx->cfg_ptr->ext_ifencei) { return false; } diff --git a/target/riscv/insn_trans/trans_rvv.c.inc b/target/riscv/insn_trans/trans_rvv.c.inc index f85a9e83b4..275fded6e4 100644 --- a/target/riscv/insn_trans/trans_rvv.c.inc +++ b/target/riscv/insn_trans/trans_rvv.c.inc @@ -74,7 +74,7 @@ static bool require_zve32f(DisasContext *s) } /* Zve32f doesn't support FP64. (Section 18.2) */ - return s->ext_zve32f ? s->sew <= MO_32 : true; + return s->cfg_ptr->ext_zve32f ? s->sew <= MO_32 : true; } static bool require_scale_zve32f(DisasContext *s) @@ -85,7 +85,7 @@ static bool require_scale_zve32f(DisasContext *s) } /* Zve32f doesn't support FP64. (Section 18.2) */ - return s->ext_zve64f ? s->sew <= MO_16 : true; + return s->cfg_ptr->ext_zve64f ? s->sew <= MO_16 : true; } static bool require_zve64f(DisasContext *s) @@ -96,7 +96,7 @@ static bool require_zve64f(DisasContext *s) } /* Zve64f doesn't support FP64. (Section 18.2) */ - return s->ext_zve64f ? s->sew <= MO_32 : true; + return s->cfg_ptr->ext_zve64f ? s->sew <= MO_32 : true; } static bool require_scale_zve64f(DisasContext *s) @@ -107,7 +107,7 @@ static bool require_scale_zve64f(DisasContext *s) } /* Zve64f doesn't support FP64. (Section 18.2) */ - return s->ext_zve64f ? s->sew <= MO_16 : true; + return s->cfg_ptr->ext_zve64f ? s->sew <= MO_16 : true; } /* Destination vector register group cannot overlap source mask register. */ @@ -174,7 +174,8 @@ static bool do_vsetvl(DisasContext *s, int rd, int rs1, TCGv s2) TCGv s1, dst; if (!require_rvv(s) || - !(has_ext(s, RVV) || s->ext_zve32f || s->ext_zve64f)) { + !(has_ext(s, RVV) || s->cfg_ptr->ext_zve32f || + s->cfg_ptr->ext_zve64f)) { return false; } @@ -210,7 +211,8 @@ static bool do_vsetivli(DisasContext *s, int rd, TCGv s1, TCGv s2) TCGv dst; if (!require_rvv(s) || - !(has_ext(s, RVV) || s->ext_zve32f || s->ext_zve64f)) { + !(has_ext(s, RVV) || s->cfg_ptr->ext_zve32f || + s->cfg_ptr->ext_zve64f)) { return false; } @@ -248,7 +250,7 @@ static bool trans_vsetivli(DisasContext *s, arg_vsetivli *a) /* vector register offset from env */ static uint32_t vreg_ofs(DisasContext *s, int reg) { - return offsetof(CPURISCVState, vreg) + reg * s->vlen / 8; + return offsetof(CPURISCVState, vreg) + reg * s->cfg_ptr->vlen / 8; } /* check functions */ @@ -318,7 +320,8 @@ static bool vext_check_st_index(DisasContext *s, int vd, int vs2, int nf, * when XLEN=32. (Section 18.2) */ if (get_xl(s) == MXL_RV32) { - ret &= (!has_ext(s, RVV) && s->ext_zve64f ? eew != MO_64 : true); + ret &= (!has_ext(s, RVV) && + s->cfg_ptr->ext_zve64f ? eew != MO_64 : true); } return ret; @@ -454,7 +457,7 @@ static bool vext_wide_check_common(DisasContext *s, int vd, int vm) { return (s->lmul <= 2) && (s->sew < MO_64) && - ((s->sew + 1) <= (s->elen >> 4)) && + ((s->sew + 1) <= (s->cfg_ptr->elen >> 4)) && require_align(vd, s->lmul + 1) && require_vm(vm, vd); } @@ -482,7 +485,7 @@ static bool vext_narrow_check_common(DisasContext *s, int vd, int vs2, { return (s->lmul <= 2) && (s->sew < MO_64) && - ((s->sew + 1) <= (s->elen >> 4)) && + ((s->sew + 1) <= (s->cfg_ptr->elen >> 4)) && require_align(vs2, s->lmul + 1) && require_align(vd, s->lmul) && require_vm(vm, vd); @@ -661,7 +664,8 @@ static bool ldst_us_trans(uint32_t vd, uint32_t rs1, uint32_t data, * The first part is vlen in bytes, encoded in maxsz of simd_desc. * The second part is lmul, encoded in data of simd_desc. */ - desc = tcg_constant_i32(simd_desc(s->vlen / 8, s->vlen / 8, data)); + desc = tcg_constant_i32(simd_desc(s->cfg_ptr->vlen / 8, + s->cfg_ptr->vlen / 8, data)); tcg_gen_addi_ptr(dest, cpu_env, vreg_ofs(s, vd)); tcg_gen_addi_ptr(mask, cpu_env, vreg_ofs(s, 0)); @@ -819,7 +823,8 @@ static bool ldst_stride_trans(uint32_t vd, uint32_t rs1, uint32_t rs2, mask = tcg_temp_new_ptr(); base = get_gpr(s, rs1, EXT_NONE); stride = get_gpr(s, rs2, EXT_NONE); - desc = tcg_constant_i32(simd_desc(s->vlen / 8, s->vlen / 8, data)); + desc = tcg_constant_i32(simd_desc(s->cfg_ptr->vlen / 8, + s->cfg_ptr->vlen / 8, data)); tcg_gen_addi_ptr(dest, cpu_env, vreg_ofs(s, vd)); tcg_gen_addi_ptr(mask, cpu_env, vreg_ofs(s, 0)); @@ -925,7 +930,8 @@ static bool ldst_index_trans(uint32_t vd, uint32_t rs1, uint32_t vs2, mask = tcg_temp_new_ptr(); index = tcg_temp_new_ptr(); base = get_gpr(s, rs1, EXT_NONE); - desc = tcg_constant_i32(simd_desc(s->vlen / 8, s->vlen / 8, data)); + desc = tcg_constant_i32(simd_desc(s->cfg_ptr->vlen / 8, + s->cfg_ptr->vlen / 8, data)); tcg_gen_addi_ptr(dest, cpu_env, vreg_ofs(s, vd)); tcg_gen_addi_ptr(index, cpu_env, vreg_ofs(s, vs2)); @@ -1065,7 +1071,8 @@ static bool ldff_trans(uint32_t vd, uint32_t rs1, uint32_t data, dest = tcg_temp_new_ptr(); mask = tcg_temp_new_ptr(); base = get_gpr(s, rs1, EXT_NONE); - desc = tcg_constant_i32(simd_desc(s->vlen / 8, s->vlen / 8, data)); + desc = tcg_constant_i32(simd_desc(s->cfg_ptr->vlen / 8, + s->cfg_ptr->vlen / 8, data)); tcg_gen_addi_ptr(dest, cpu_env, vreg_ofs(s, vd)); tcg_gen_addi_ptr(mask, cpu_env, vreg_ofs(s, 0)); @@ -1120,7 +1127,8 @@ static bool ldst_whole_trans(uint32_t vd, uint32_t rs1, uint32_t nf, uint32_t data = FIELD_DP32(0, VDATA, NF, nf); dest = tcg_temp_new_ptr(); - desc = tcg_constant_i32(simd_desc(s->vlen / 8, s->vlen / 8, data)); + desc = tcg_constant_i32(simd_desc(s->cfg_ptr->vlen / 8, + s->cfg_ptr->vlen / 8, data)); base = get_gpr(s, rs1, EXT_NONE); tcg_gen_addi_ptr(dest, cpu_env, vreg_ofs(s, vd)); @@ -1185,7 +1193,7 @@ GEN_LDST_WHOLE_TRANS(vs8r_v, 8, true) static inline uint32_t MAXSZ(DisasContext *s) { int scale = s->lmul - 3; - return scale < 0 ? s->vlen >> -scale : s->vlen << scale; + return scale < 0 ? s->cfg_ptr->vlen >> -scale : s->cfg_ptr->vlen << scale; } static bool opivv_check(DisasContext *s, arg_rmrr *a) @@ -1220,7 +1228,8 @@ do_opivv_gvec(DisasContext *s, arg_rmrr *a, GVecGen3Fn *gvec_fn, data = FIELD_DP32(data, VDATA, LMUL, s->lmul); tcg_gen_gvec_4_ptr(vreg_ofs(s, a->rd), vreg_ofs(s, 0), vreg_ofs(s, a->rs1), vreg_ofs(s, a->rs2), - cpu_env, s->vlen / 8, s->vlen / 8, data, fn); + cpu_env, s->cfg_ptr->vlen / 8, + s->cfg_ptr->vlen / 8, data, fn); } mark_vs_dirty(s); gen_set_label(over); @@ -1262,7 +1271,8 @@ static bool opivx_trans(uint32_t vd, uint32_t rs1, uint32_t vs2, uint32_t vm, data = FIELD_DP32(data, VDATA, VM, vm); data = FIELD_DP32(data, VDATA, LMUL, s->lmul); - desc = tcg_constant_i32(simd_desc(s->vlen / 8, s->vlen / 8, data)); + desc = tcg_constant_i32(simd_desc(s->cfg_ptr->vlen / 8, + s->cfg_ptr->vlen / 8, data)); tcg_gen_addi_ptr(dest, cpu_env, vreg_ofs(s, vd)); tcg_gen_addi_ptr(src2, cpu_env, vreg_ofs(s, vs2)); @@ -1425,7 +1435,8 @@ static bool opivi_trans(uint32_t vd, uint32_t imm, uint32_t vs2, uint32_t vm, data = FIELD_DP32(data, VDATA, VM, vm); data = FIELD_DP32(data, VDATA, LMUL, s->lmul); - desc = tcg_constant_i32(simd_desc(s->vlen / 8, s->vlen / 8, data)); + desc = tcg_constant_i32(simd_desc(s->cfg_ptr->vlen / 8, + s->cfg_ptr->vlen / 8, data)); tcg_gen_addi_ptr(dest, cpu_env, vreg_ofs(s, vd)); tcg_gen_addi_ptr(src2, cpu_env, vreg_ofs(s, vs2)); @@ -1508,7 +1519,8 @@ static bool do_opivv_widen(DisasContext *s, arg_rmrr *a, tcg_gen_gvec_4_ptr(vreg_ofs(s, a->rd), vreg_ofs(s, 0), vreg_ofs(s, a->rs1), vreg_ofs(s, a->rs2), - cpu_env, s->vlen / 8, s->vlen / 8, + cpu_env, s->cfg_ptr->vlen / 8, + s->cfg_ptr->vlen / 8, data, fn); mark_vs_dirty(s); gen_set_label(over); @@ -1587,7 +1599,8 @@ static bool do_opiwv_widen(DisasContext *s, arg_rmrr *a, tcg_gen_gvec_4_ptr(vreg_ofs(s, a->rd), vreg_ofs(s, 0), vreg_ofs(s, a->rs1), vreg_ofs(s, a->rs2), - cpu_env, s->vlen / 8, s->vlen / 8, data, fn); + cpu_env, s->cfg_ptr->vlen / 8, + s->cfg_ptr->vlen / 8, data, fn); mark_vs_dirty(s); gen_set_label(over); return true; @@ -1663,7 +1676,8 @@ static bool trans_##NAME(DisasContext *s, arg_rmrr *a) \ tcg_gen_gvec_4_ptr(vreg_ofs(s, a->rd), vreg_ofs(s, 0), \ vreg_ofs(s, a->rs1), \ vreg_ofs(s, a->rs2), cpu_env, \ - s->vlen / 8, s->vlen / 8, data, \ + s->cfg_ptr->vlen / 8, \ + s->cfg_ptr->vlen / 8, data, \ fns[s->sew]); \ mark_vs_dirty(s); \ gen_set_label(over); \ @@ -1843,7 +1857,8 @@ static bool trans_##NAME(DisasContext *s, arg_rmrr *a) \ tcg_gen_gvec_4_ptr(vreg_ofs(s, a->rd), vreg_ofs(s, 0), \ vreg_ofs(s, a->rs1), \ vreg_ofs(s, a->rs2), cpu_env, \ - s->vlen / 8, s->vlen / 8, data, \ + s->cfg_ptr->vlen / 8, \ + s->cfg_ptr->vlen / 8, data, \ fns[s->sew]); \ mark_vs_dirty(s); \ gen_set_label(over); \ @@ -1963,7 +1978,8 @@ static bool vmulh_vv_check(DisasContext *s, arg_rmrr *a) * are not included for EEW=64 in Zve64*. (Section 18.2) */ return opivv_check(s, a) && - (!has_ext(s, RVV) && s->ext_zve64f ? s->sew != MO_64 : true); + (!has_ext(s, RVV) && + s->cfg_ptr->ext_zve64f ? s->sew != MO_64 : true); } static bool vmulh_vx_check(DisasContext *s, arg_rmrr *a) @@ -1976,7 +1992,8 @@ static bool vmulh_vx_check(DisasContext *s, arg_rmrr *a) * are not included for EEW=64 in Zve64*. (Section 18.2) */ return opivx_check(s, a) && - (!has_ext(s, RVV) && s->ext_zve64f ? s->sew != MO_64 : true); + (!has_ext(s, RVV) && + s->cfg_ptr->ext_zve64f ? s->sew != MO_64 : true); } GEN_OPIVV_GVEC_TRANS(vmul_vv, mul) @@ -2046,7 +2063,8 @@ static bool trans_vmv_v_v(DisasContext *s, arg_vmv_v_v *a) tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_vl, 0, over); tcg_gen_gvec_2_ptr(vreg_ofs(s, a->rd), vreg_ofs(s, a->rs1), - cpu_env, s->vlen / 8, s->vlen / 8, data, + cpu_env, s->cfg_ptr->vlen / 8, + s->cfg_ptr->vlen / 8, data, fns[s->sew]); gen_set_label(over); } @@ -2083,7 +2101,8 @@ static bool trans_vmv_v_x(DisasContext *s, arg_vmv_v_x *a) }; tcg_gen_ext_tl_i64(s1_i64, s1); - desc = tcg_constant_i32(simd_desc(s->vlen / 8, s->vlen / 8, data)); + desc = tcg_constant_i32(simd_desc(s->cfg_ptr->vlen / 8, + s->cfg_ptr->vlen / 8, data)); tcg_gen_addi_ptr(dest, cpu_env, vreg_ofs(s, a->rd)); fns[s->sew](dest, s1_i64, cpu_env, desc); @@ -2123,7 +2142,8 @@ static bool trans_vmv_v_i(DisasContext *s, arg_vmv_v_i *a) s1 = tcg_constant_i64(simm); dest = tcg_temp_new_ptr(); - desc = tcg_constant_i32(simd_desc(s->vlen / 8, s->vlen / 8, data)); + desc = tcg_constant_i32(simd_desc(s->cfg_ptr->vlen / 8, + s->cfg_ptr->vlen / 8, data)); tcg_gen_addi_ptr(dest, cpu_env, vreg_ofs(s, a->rd)); fns[s->sew](dest, s1, cpu_env, desc); @@ -2176,7 +2196,8 @@ static bool vsmul_vv_check(DisasContext *s, arg_rmrr *a) * for EEW=64 in Zve64*. (Section 18.2) */ return opivv_check(s, a) && - (!has_ext(s, RVV) && s->ext_zve64f ? s->sew != MO_64 : true); + (!has_ext(s, RVV) && + s->cfg_ptr->ext_zve64f ? s->sew != MO_64 : true); } static bool vsmul_vx_check(DisasContext *s, arg_rmrr *a) @@ -2187,7 +2208,8 @@ static bool vsmul_vx_check(DisasContext *s, arg_rmrr *a) * for EEW=64 in Zve64*. (Section 18.2) */ return opivx_check(s, a) && - (!has_ext(s, RVV) && s->ext_zve64f ? s->sew != MO_64 : true); + (!has_ext(s, RVV) && + s->cfg_ptr->ext_zve64f ? s->sew != MO_64 : true); } GEN_OPIVV_TRANS(vsmul_vv, vsmul_vv_check) @@ -2275,7 +2297,8 @@ static bool trans_##NAME(DisasContext *s, arg_rmrr *a) \ tcg_gen_gvec_4_ptr(vreg_ofs(s, a->rd), vreg_ofs(s, 0), \ vreg_ofs(s, a->rs1), \ vreg_ofs(s, a->rs2), cpu_env, \ - s->vlen / 8, s->vlen / 8, data, \ + s->cfg_ptr->vlen / 8, \ + s->cfg_ptr->vlen / 8, data, \ fns[s->sew - 1]); \ mark_vs_dirty(s); \ gen_set_label(over); \ @@ -2302,7 +2325,8 @@ static bool opfvf_trans(uint32_t vd, uint32_t rs1, uint32_t vs2, dest = tcg_temp_new_ptr(); mask = tcg_temp_new_ptr(); src2 = tcg_temp_new_ptr(); - desc = tcg_constant_i32(simd_desc(s->vlen / 8, s->vlen / 8, data)); + desc = tcg_constant_i32(simd_desc(s->cfg_ptr->vlen / 8, + s->cfg_ptr->vlen / 8, data)); tcg_gen_addi_ptr(dest, cpu_env, vreg_ofs(s, vd)); tcg_gen_addi_ptr(src2, cpu_env, vreg_ofs(s, vs2)); @@ -2391,7 +2415,8 @@ static bool trans_##NAME(DisasContext *s, arg_rmrr *a) \ tcg_gen_gvec_4_ptr(vreg_ofs(s, a->rd), vreg_ofs(s, 0), \ vreg_ofs(s, a->rs1), \ vreg_ofs(s, a->rs2), cpu_env, \ - s->vlen / 8, s->vlen / 8, data, \ + s->cfg_ptr->vlen / 8, \ + s->cfg_ptr->vlen / 8, data, \ fns[s->sew - 1]); \ mark_vs_dirty(s); \ gen_set_label(over); \ @@ -2464,7 +2489,8 @@ static bool trans_##NAME(DisasContext *s, arg_rmrr *a) \ tcg_gen_gvec_4_ptr(vreg_ofs(s, a->rd), vreg_ofs(s, 0), \ vreg_ofs(s, a->rs1), \ vreg_ofs(s, a->rs2), cpu_env, \ - s->vlen / 8, s->vlen / 8, data, \ + s->cfg_ptr->vlen / 8, \ + s->cfg_ptr->vlen / 8, data, \ fns[s->sew - 1]); \ mark_vs_dirty(s); \ gen_set_label(over); \ @@ -2583,7 +2609,8 @@ static bool do_opfv(DisasContext *s, arg_rmr *a, data = FIELD_DP32(data, VDATA, LMUL, s->lmul); tcg_gen_gvec_3_ptr(vreg_ofs(s, a->rd), vreg_ofs(s, 0), vreg_ofs(s, a->rs2), cpu_env, - s->vlen / 8, s->vlen / 8, data, fn); + s->cfg_ptr->vlen / 8, + s->cfg_ptr->vlen / 8, data, fn); mark_vs_dirty(s); gen_set_label(over); return true; @@ -2696,7 +2723,8 @@ static bool trans_vfmv_v_f(DisasContext *s, arg_vfmv_v_f *a) do_nanbox(s, t1, cpu_fpr[a->rs1]); dest = tcg_temp_new_ptr(); - desc = tcg_constant_i32(simd_desc(s->vlen / 8, s->vlen / 8, data)); + desc = tcg_constant_i32(simd_desc(s->cfg_ptr->vlen / 8, + s->cfg_ptr->vlen / 8, data)); tcg_gen_addi_ptr(dest, cpu_env, vreg_ofs(s, a->rd)); fns[s->sew - 1](dest, t1, cpu_env, desc); @@ -2782,7 +2810,8 @@ static bool trans_##NAME(DisasContext *s, arg_rmr *a) \ data = FIELD_DP32(data, VDATA, LMUL, s->lmul); \ tcg_gen_gvec_3_ptr(vreg_ofs(s, a->rd), vreg_ofs(s, 0), \ vreg_ofs(s, a->rs2), cpu_env, \ - s->vlen / 8, s->vlen / 8, data, \ + s->cfg_ptr->vlen / 8, \ + s->cfg_ptr->vlen / 8, data, \ fns[s->sew - 1]); \ mark_vs_dirty(s); \ gen_set_label(over); \ @@ -2831,7 +2860,8 @@ static bool trans_##NAME(DisasContext *s, arg_rmr *a) \ data = FIELD_DP32(data, VDATA, VM, a->vm); \ tcg_gen_gvec_3_ptr(vreg_ofs(s, a->rd), vreg_ofs(s, 0), \ vreg_ofs(s, a->rs2), cpu_env, \ - s->vlen / 8, s->vlen / 8, data, \ + s->cfg_ptr->vlen / 8, \ + s->cfg_ptr->vlen / 8, data, \ fns[s->sew]); \ mark_vs_dirty(s); \ gen_set_label(over); \ @@ -2896,7 +2926,8 @@ static bool trans_##NAME(DisasContext *s, arg_rmr *a) \ data = FIELD_DP32(data, VDATA, LMUL, s->lmul); \ tcg_gen_gvec_3_ptr(vreg_ofs(s, a->rd), vreg_ofs(s, 0), \ vreg_ofs(s, a->rs2), cpu_env, \ - s->vlen / 8, s->vlen / 8, data, \ + s->cfg_ptr->vlen / 8, \ + s->cfg_ptr->vlen / 8, data, \ fns[s->sew - 1]); \ mark_vs_dirty(s); \ gen_set_label(over); \ @@ -2947,7 +2978,8 @@ static bool trans_##NAME(DisasContext *s, arg_rmr *a) \ data = FIELD_DP32(data, VDATA, VM, a->vm); \ tcg_gen_gvec_3_ptr(vreg_ofs(s, a->rd), vreg_ofs(s, 0), \ vreg_ofs(s, a->rs2), cpu_env, \ - s->vlen / 8, s->vlen / 8, data, \ + s->cfg_ptr->vlen / 8, \ + s->cfg_ptr->vlen / 8, data, \ fns[s->sew]); \ mark_vs_dirty(s); \ gen_set_label(over); \ @@ -2986,7 +3018,7 @@ GEN_OPIVV_TRANS(vredxor_vs, reduction_check) static bool reduction_widen_check(DisasContext *s, arg_rmrr *a) { return reduction_check(s, a) && (s->sew < MO_64) && - ((s->sew + 1) <= (s->elen >> 4)); + ((s->sew + 1) <= (s->cfg_ptr->elen >> 4)); } GEN_OPIVV_WIDEN_TRANS(vwredsum_vs, reduction_widen_check) @@ -3034,7 +3066,8 @@ static bool trans_##NAME(DisasContext *s, arg_r *a) \ tcg_gen_gvec_4_ptr(vreg_ofs(s, a->rd), vreg_ofs(s, 0), \ vreg_ofs(s, a->rs1), \ vreg_ofs(s, a->rs2), cpu_env, \ - s->vlen / 8, s->vlen / 8, data, fn); \ + s->cfg_ptr->vlen / 8, \ + s->cfg_ptr->vlen / 8, data, fn); \ mark_vs_dirty(s); \ gen_set_label(over); \ return true; \ @@ -3067,7 +3100,8 @@ static bool trans_vcpop_m(DisasContext *s, arg_rmr *a) mask = tcg_temp_new_ptr(); src2 = tcg_temp_new_ptr(); dst = dest_gpr(s, a->rd); - desc = tcg_constant_i32(simd_desc(s->vlen / 8, s->vlen / 8, data)); + desc = tcg_constant_i32(simd_desc(s->cfg_ptr->vlen / 8, + s->cfg_ptr->vlen / 8, data)); tcg_gen_addi_ptr(src2, cpu_env, vreg_ofs(s, a->rs2)); tcg_gen_addi_ptr(mask, cpu_env, vreg_ofs(s, 0)); @@ -3099,7 +3133,8 @@ static bool trans_vfirst_m(DisasContext *s, arg_rmr *a) mask = tcg_temp_new_ptr(); src2 = tcg_temp_new_ptr(); dst = dest_gpr(s, a->rd); - desc = tcg_constant_i32(simd_desc(s->vlen / 8, s->vlen / 8, data)); + desc = tcg_constant_i32(simd_desc(s->cfg_ptr->vlen / 8, + s->cfg_ptr->vlen / 8, data)); tcg_gen_addi_ptr(src2, cpu_env, vreg_ofs(s, a->rs2)); tcg_gen_addi_ptr(mask, cpu_env, vreg_ofs(s, 0)); @@ -3134,7 +3169,8 @@ static bool trans_##NAME(DisasContext *s, arg_rmr *a) \ data = FIELD_DP32(data, VDATA, LMUL, s->lmul); \ tcg_gen_gvec_3_ptr(vreg_ofs(s, a->rd), \ vreg_ofs(s, 0), vreg_ofs(s, a->rs2), \ - cpu_env, s->vlen / 8, s->vlen / 8, \ + cpu_env, s->cfg_ptr->vlen / 8, \ + s->cfg_ptr->vlen / 8, \ data, fn); \ mark_vs_dirty(s); \ gen_set_label(over); \ @@ -3174,7 +3210,8 @@ static bool trans_viota_m(DisasContext *s, arg_viota_m *a) }; tcg_gen_gvec_3_ptr(vreg_ofs(s, a->rd), vreg_ofs(s, 0), vreg_ofs(s, a->rs2), cpu_env, - s->vlen / 8, s->vlen / 8, data, fns[s->sew]); + s->cfg_ptr->vlen / 8, + s->cfg_ptr->vlen / 8, data, fns[s->sew]); mark_vs_dirty(s); gen_set_label(over); return true; @@ -3200,7 +3237,8 @@ static bool trans_vid_v(DisasContext *s, arg_vid_v *a) gen_helper_vid_v_w, gen_helper_vid_v_d, }; tcg_gen_gvec_2_ptr(vreg_ofs(s, a->rd), vreg_ofs(s, 0), - cpu_env, s->vlen / 8, s->vlen / 8, + cpu_env, s->cfg_ptr->vlen / 8, + s->cfg_ptr->vlen / 8, data, fns[s->sew]); mark_vs_dirty(s); gen_set_label(over); @@ -3554,7 +3592,8 @@ static bool trans_vrgather_vx(DisasContext *s, arg_rmrr *a) if (a->vm && s->vl_eq_vlmax) { int scale = s->lmul - (s->sew + 3); - int vlmax = scale < 0 ? s->vlen >> -scale : s->vlen << scale; + int vlmax = scale < 0 ? + s->cfg_ptr->vlen >> -scale : s->cfg_ptr->vlen << scale; TCGv_i64 dest = tcg_temp_new_i64(); if (a->rs1 == 0) { @@ -3586,7 +3625,8 @@ static bool trans_vrgather_vi(DisasContext *s, arg_rmrr *a) if (a->vm && s->vl_eq_vlmax) { int scale = s->lmul - (s->sew + 3); - int vlmax = scale < 0 ? s->vlen >> -scale : s->vlen << scale; + int vlmax = scale < 0 ? + s->cfg_ptr->vlen >> -scale : s->cfg_ptr->vlen << scale; if (a->rs1 >= vlmax) { tcg_gen_gvec_dup_imm(MO_64, vreg_ofs(s, a->rd), MAXSZ(s), MAXSZ(s), 0); @@ -3638,7 +3678,8 @@ static bool trans_vcompress_vm(DisasContext *s, arg_r *a) data = FIELD_DP32(data, VDATA, LMUL, s->lmul); tcg_gen_gvec_4_ptr(vreg_ofs(s, a->rd), vreg_ofs(s, 0), vreg_ofs(s, a->rs1), vreg_ofs(s, a->rs2), - cpu_env, s->vlen / 8, s->vlen / 8, data, + cpu_env, s->cfg_ptr->vlen / 8, + s->cfg_ptr->vlen / 8, data, fns[s->sew]); mark_vs_dirty(s); gen_set_label(over); @@ -3657,7 +3698,7 @@ static bool trans_##NAME(DisasContext *s, arg_##NAME * a) \ if (require_rvv(s) && \ QEMU_IS_ALIGNED(a->rd, LEN) && \ QEMU_IS_ALIGNED(a->rs2, LEN)) { \ - uint32_t maxsz = (s->vlen >> 3) * LEN; \ + uint32_t maxsz = (s->cfg_ptr->vlen >> 3) * LEN; \ if (s->vstart == 0) { \ /* EEW = 8 */ \ tcg_gen_gvec_mov(MO_8, vreg_ofs(s, a->rd), \ @@ -3742,7 +3783,8 @@ static bool int_ext_op(DisasContext *s, arg_rmr *a, uint8_t seq) tcg_gen_gvec_3_ptr(vreg_ofs(s, a->rd), vreg_ofs(s, 0), vreg_ofs(s, a->rs2), cpu_env, - s->vlen / 8, s->vlen / 8, data, fn); + s->cfg_ptr->vlen / 8, + s->cfg_ptr->vlen / 8, data, fn); mark_vs_dirty(s); gen_set_label(over); diff --git a/target/riscv/insn_trans/trans_rvzfh.c.inc b/target/riscv/insn_trans/trans_rvzfh.c.inc index 5a7cac8958..608c51da2c 100644 --- a/target/riscv/insn_trans/trans_rvzfh.c.inc +++ b/target/riscv/insn_trans/trans_rvzfh.c.inc @@ -17,13 +17,13 @@ */ #define REQUIRE_ZFH(ctx) do { \ - if (!ctx->ext_zfh) { \ + if (!ctx->cfg_ptr->ext_zfh) { \ return false; \ } \ } while (0) #define REQUIRE_ZFH_OR_ZFHMIN(ctx) do { \ - if (!(ctx->ext_zfh || ctx->ext_zfhmin)) { \ + if (!(ctx->cfg_ptr->ext_zfh || ctx->cfg_ptr->ext_zfhmin)) { \ return false; \ } \ } while (0) diff --git a/target/riscv/translate.c b/target/riscv/translate.c index 49e40735ce..f19d5cd0c0 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -77,11 +77,6 @@ typedef struct DisasContext { RISCVMXL ol; bool virt_enabled; const RISCVCPUConfig *cfg_ptr; - bool ext_ifencei; - bool ext_zfh; - bool ext_zfhmin; - bool ext_zve32f; - bool ext_zve64f; bool hlsx; /* vector extension */ bool vill; @@ -99,8 +94,6 @@ typedef struct DisasContext { */ int8_t lmul; uint8_t sew; - uint16_t vlen; - uint16_t elen; target_ulong vstart; bool vl_eq_vlmax; uint8_t ntemp; @@ -910,13 +903,6 @@ static void riscv_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) ctx->misa_ext = env->misa_ext; ctx->frm = -1; /* unknown rounding mode */ ctx->cfg_ptr = &(cpu->cfg); - ctx->ext_ifencei = cpu->cfg.ext_ifencei; - ctx->ext_zfh = cpu->cfg.ext_zfh; - ctx->ext_zfhmin = cpu->cfg.ext_zfhmin; - ctx->ext_zve32f = cpu->cfg.ext_zve32f; - ctx->ext_zve64f = cpu->cfg.ext_zve64f; - ctx->vlen = cpu->cfg.vlen; - ctx->elen = cpu->cfg.elen; ctx->mstatus_hs_fs = FIELD_EX32(tb_flags, TB_FLAGS, MSTATUS_HS_FS); ctx->mstatus_hs_vs = FIELD_EX32(tb_flags, TB_FLAGS, MSTATUS_HS_VS); ctx->hlsx = FIELD_EX32(tb_flags, TB_FLAGS, HLSX); From f2a32bec8f0da993f67698b6c7ebd60e0f19622e Mon Sep 17 00:00:00 2001 From: Philipp Tomsich Date: Wed, 2 Feb 2022 01:52:46 +0100 Subject: [PATCH 432/460] target/riscv: access cfg structure through DisasContext The Zb[abcs] support code still uses the RISCV_CPU macros to access the configuration information (i.e., check whether an extension is available/enabled). Now that we provide this information directly from DisasContext, we can access this directly via the cfg_ptr field. Signed-off-by: Philipp Tomsich Reviewed-by: Alistair Francis Suggested-by: Richard Henderson Reviewed-by: Richard Henderson Message-Id: <20220202005249.3566542-5-philipp.tomsich@vrull.eu> Signed-off-by: Alistair Francis --- target/riscv/insn_trans/trans_rvb.c.inc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/target/riscv/insn_trans/trans_rvb.c.inc b/target/riscv/insn_trans/trans_rvb.c.inc index 810431a1d6..f9bd3b7ec4 100644 --- a/target/riscv/insn_trans/trans_rvb.c.inc +++ b/target/riscv/insn_trans/trans_rvb.c.inc @@ -19,25 +19,25 @@ */ #define REQUIRE_ZBA(ctx) do { \ - if (!RISCV_CPU(ctx->cs)->cfg.ext_zba) { \ + if (ctx->cfg_ptr->ext_zba) { \ return false; \ } \ } while (0) #define REQUIRE_ZBB(ctx) do { \ - if (!RISCV_CPU(ctx->cs)->cfg.ext_zbb) { \ + if (ctx->cfg_ptr->ext_zbb) { \ return false; \ } \ } while (0) #define REQUIRE_ZBC(ctx) do { \ - if (!RISCV_CPU(ctx->cs)->cfg.ext_zbc) { \ + if (ctx->cfg_ptr->ext_zbc) { \ return false; \ } \ } while (0) #define REQUIRE_ZBS(ctx) do { \ - if (!RISCV_CPU(ctx->cs)->cfg.ext_zbs) { \ + if (ctx->cfg_ptr->ext_zbs) { \ return false; \ } \ } while (0) From 5e199b6bdc544658ecc9d614779b2cf3fe215ead Mon Sep 17 00:00:00 2001 From: Philipp Tomsich Date: Wed, 2 Feb 2022 01:52:47 +0100 Subject: [PATCH 433/460] target/riscv: iterate over a table of decoders To split up the decoder into multiple functions (both to support vendor-specific opcodes in separate files and to simplify maintenance of orthogonal extensions), this changes decode_op to iterate over a table of decoders predicated on guard functions. This commit only adds the new structure and the table, allowing for the easy addition of additional decoders in the future. Signed-off-by: Philipp Tomsich Reviewed-by: Richard Henderson Reviewed-by: Alistair Francis Message-Id: <20220202005249.3566542-6-philipp.tomsich@vrull.eu> Signed-off-by: Alistair Francis --- target/riscv/translate.c | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/target/riscv/translate.c b/target/riscv/translate.c index f19d5cd0c0..30b1b68341 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -111,6 +111,11 @@ static inline bool has_ext(DisasContext *ctx, uint32_t ext) return ctx->misa_ext & ext; } +static bool always_true_p(DisasContext *ctx __attribute__((__unused__))) +{ + return true; +} + #ifdef TARGET_RISCV32 #define get_xl(ctx) MXL_RV32 #elif defined(CONFIG_USER_ONLY) @@ -855,15 +860,26 @@ static uint32_t opcode_at(DisasContextBase *dcbase, target_ulong pc) static void decode_opc(CPURISCVState *env, DisasContext *ctx, uint16_t opcode) { - /* check for compressed insn */ + /* + * 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)(DisasContext *); + bool (*decode_func)(DisasContext *, uint32_t); + } decoders[] = { + { always_true_p, decode_insn32 }, + }; + + /* Check for compressed insn */ if (extract16(opcode, 0, 2) != 3) { if (!has_ext(ctx, RVC)) { gen_exception_illegal(ctx); } else { ctx->opcode = opcode; ctx->pc_succ_insn = ctx->base.pc_next + 2; - if (!decode_insn16(ctx, opcode)) { - gen_exception_illegal(ctx); + if (decode_insn16(ctx, opcode)) { + return; } } } else { @@ -873,10 +889,16 @@ static void decode_opc(CPURISCVState *env, DisasContext *ctx, uint16_t opcode) ctx->base.pc_next + 2)); ctx->opcode = opcode32; ctx->pc_succ_insn = ctx->base.pc_next + 4; - if (!decode_insn32(ctx, opcode32)) { - gen_exception_illegal(ctx); + + for (size_t i = 0; i < ARRAY_SIZE(decoders); ++i) { + if (decoders[i].guard_func(ctx) && + decoders[i].decode_func(ctx, opcode32)) { + return; + } } } + + gen_exception_illegal(ctx); } static void riscv_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) From 0d429bd243dd391e844213d97bb14a0f119b33b5 Mon Sep 17 00:00:00 2001 From: Philipp Tomsich Date: Wed, 2 Feb 2022 01:52:48 +0100 Subject: [PATCH 434/460] target/riscv: Add XVentanaCondOps custom extension This adds the decoder and translation for the XVentanaCondOps custom extension (vendor-defined by Ventana Micro Systems), which is documented at https://github.com/ventanamicro/ventana-custom-extensions/releases/download/v1.0.0/ventana-custom-extensions-v1.0.0.pdf This commit then also adds a guard-function (has_XVentanaCondOps_p) and the decoder function to the table of decoders, enabling the support for the XVentanaCondOps extension. Signed-off-by: Philipp Tomsich Reviewed-by: Richard Henderson Reviewed-by: Alistair Francis Message-Id: <20220202005249.3566542-7-philipp.tomsich@vrull.eu> Signed-off-by: Alistair Francis --- target/riscv/XVentanaCondOps.decode | 25 ++++++++++++ target/riscv/cpu.c | 3 ++ target/riscv/cpu.h | 3 ++ .../insn_trans/trans_xventanacondops.c.inc | 39 +++++++++++++++++++ target/riscv/meson.build | 1 + target/riscv/translate.c | 12 ++++++ 6 files changed, 83 insertions(+) create mode 100644 target/riscv/XVentanaCondOps.decode create mode 100644 target/riscv/insn_trans/trans_xventanacondops.c.inc diff --git a/target/riscv/XVentanaCondOps.decode b/target/riscv/XVentanaCondOps.decode new file mode 100644 index 0000000000..5aef7c3d72 --- /dev/null +++ b/target/riscv/XVentanaCondOps.decode @@ -0,0 +1,25 @@ +# +# RISC-V translation routines for the XVentanaCondOps extension +# +# Copyright (c) 2022 Dr. Philipp Tomsich, philipp.tomsich@vrull.eu +# +# SPDX-License-Identifier: LGPL-2.1-or-later +# +# Reference: VTx-family custom instructions +# Custom ISA extensions for Ventana Micro Systems RISC-V cores +# (https://github.com/ventanamicro/ventana-custom-extensions/releases/download/v1.0.0/ventana-custom-extensions-v1.0.0.pdf) + +# Fields +%rs2 20:5 +%rs1 15:5 +%rd 7:5 + +# Argument sets +&r rd rs1 rs2 !extern + +# Formats +@r ....... ..... ..... ... ..... ....... &r %rs2 %rs1 %rd + +# *** RV64 Custom-3 Extension *** +vt_maskc 0000000 ..... ..... 110 ..... 1111011 @r +vt_maskcn 0000000 ..... ..... 111 ..... 1111011 @r diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 5ada71e5bf..1238aabe3f 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -733,6 +733,9 @@ static Property riscv_cpu_properties[] = { DEFINE_PROP_BOOL("zbc", RISCVCPU, cfg.ext_zbc, true), DEFINE_PROP_BOOL("zbs", RISCVCPU, cfg.ext_zbs, true), + /* Vendor-specific custom extensions */ + DEFINE_PROP_BOOL("xventanacondops", RISCVCPU, cfg.ext_XVentanaCondOps, false), + /* These are experimental so mark with 'x-' */ DEFINE_PROP_BOOL("x-j", RISCVCPU, cfg.ext_j, false), /* ePMP 0.9.3 */ diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 1175915c0d..aacc997d56 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -329,6 +329,9 @@ struct RISCVCPUConfig { bool ext_zve32f; bool ext_zve64f; + /* Vendor-specific custom extensions */ + bool ext_XVentanaCondOps; + char *priv_spec; char *user_spec; char *bext_spec; diff --git a/target/riscv/insn_trans/trans_xventanacondops.c.inc b/target/riscv/insn_trans/trans_xventanacondops.c.inc new file mode 100644 index 0000000000..16849e6d4e --- /dev/null +++ b/target/riscv/insn_trans/trans_xventanacondops.c.inc @@ -0,0 +1,39 @@ +/* + * RISC-V translation routines for the XVentanaCondOps extension. + * + * Copyright (c) 2021-2022 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 . + */ + +static bool gen_vt_condmask(DisasContext *ctx, arg_r *a, TCGCond cond) +{ + TCGv dest = dest_gpr(ctx, a->rd); + TCGv src1 = get_gpr(ctx, a->rs1, EXT_NONE); + TCGv src2 = get_gpr(ctx, a->rs2, EXT_NONE); + + tcg_gen_movcond_tl(cond, dest, src2, ctx->zero, src1, ctx->zero); + + gen_set_gpr(ctx, a->rd, dest); + return true; +} + +static bool trans_vt_maskc(DisasContext *ctx, arg_r *a) +{ + return gen_vt_condmask(ctx, a, TCG_COND_NE); +} + +static bool trans_vt_maskcn(DisasContext *ctx, arg_r *a) +{ + return gen_vt_condmask(ctx, a, TCG_COND_EQ); +} diff --git a/target/riscv/meson.build b/target/riscv/meson.build index a3997ed580..91f0ac32ff 100644 --- a/target/riscv/meson.build +++ b/target/riscv/meson.build @@ -4,6 +4,7 @@ dir = meson.current_source_dir() gen = [ decodetree.process('insn16.decode', extra_args: ['--static-decode=decode_insn16', '--insnwidth=16']), decodetree.process('insn32.decode', extra_args: '--static-decode=decode_insn32'), + decodetree.process('XVentanaCondOps.decode', extra_args: '--static-decode=decode_XVentanaCodeOps'), ] riscv_ss = ss.source_set() diff --git a/target/riscv/translate.c b/target/riscv/translate.c index 30b1b68341..eaf5a72c81 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -116,6 +116,14 @@ static bool always_true_p(DisasContext *ctx __attribute__((__unused__))) return true; } +#define MATERIALISE_EXT_PREDICATE(ext) \ + static bool has_ ## ext ## _p(DisasContext *ctx) \ + { \ + return ctx->cfg_ptr->ext_ ## ext ; \ + } + +MATERIALISE_EXT_PREDICATE(XVentanaCondOps); + #ifdef TARGET_RISCV32 #define get_xl(ctx) MXL_RV32 #elif defined(CONFIG_USER_ONLY) @@ -854,9 +862,12 @@ static uint32_t opcode_at(DisasContextBase *dcbase, target_ulong pc) #include "insn_trans/trans_rvb.c.inc" #include "insn_trans/trans_rvzfh.c.inc" #include "insn_trans/trans_privileged.c.inc" +#include "insn_trans/trans_xventanacondops.c.inc" /* Include the auto-generated decoder for 16 bit insn */ #include "decode-insn16.c.inc" +/* Include decoders for factored-out extensions */ +#include "decode-XVentanaCondOps.c.inc" static void decode_opc(CPURISCVState *env, DisasContext *ctx, uint16_t opcode) { @@ -869,6 +880,7 @@ static void decode_opc(CPURISCVState *env, DisasContext *ctx, uint16_t opcode) bool (*decode_func)(DisasContext *, uint32_t); } decoders[] = { { always_true_p, decode_insn32 }, + { has_XVentanaCondOps_p, decode_XVentanaCodeOps }, }; /* Check for compressed insn */ From 34888f01b7a905bbbddf1fc8f41691f43b94fcd6 Mon Sep 17 00:00:00 2001 From: Philipp Tomsich Date: Wed, 2 Feb 2022 01:52:49 +0100 Subject: [PATCH 435/460] target/riscv: add a MAINTAINERS entry for XVentanaCondOps The XVentanaCondOps extension is supported by VRULL on behalf of the Ventana Micro. Add myself as a point-of-contact. Signed-off-by: Philipp Tomsich Reviewed-by: Richard Henderson Reviewed-by: Alistair Francis Message-Id: <20220202005249.3566542-8-philipp.tomsich@vrull.eu> Signed-off-by: Alistair Francis --- MAINTAINERS | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 4b3ae2ab08..81aa31b5e1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -286,6 +286,13 @@ F: include/hw/riscv/ F: linux-user/host/riscv32/ F: linux-user/host/riscv64/ +RISC-V XVentanaCondOps extension +M: Philipp Tomsich +L: qemu-riscv@nongnu.org +S: Supported +F: target/riscv/XVentanaCondOps.decode +F: target/riscv/insn_trans/trans_xventanacondops.c.inc + RENESAS RX CPUs R: Yoshinori Sato S: Orphan From ac6bcf4d467a091b11ece782f4bf0a41e0f59cef Mon Sep 17 00:00:00 2001 From: LIU Zhiwei Date: Tue, 1 Feb 2022 14:46:01 +0800 Subject: [PATCH 436/460] target/riscv: Fix vill field write in vtype The guest should be able to set the vill bit as part of vsetvl. Currently we may set env->vill to 1 in the vsetvl helper, but there is nowhere that we set it to 0, so once it transitions to 1 it's stuck there until the system is reset. Signed-off-by: LIU Zhiwei Reviewed-by: Richard Henderson Reviewed-by: Alistair Francis Message-Id: <20220201064601.41143-1-zhiwei_liu@c-sky.com> Signed-off-by: Alistair Francis --- target/riscv/vector_helper.c | 1 + 1 file changed, 1 insertion(+) diff --git a/target/riscv/vector_helper.c b/target/riscv/vector_helper.c index 020d2e841f..3bd4aac9c9 100644 --- a/target/riscv/vector_helper.c +++ b/target/riscv/vector_helper.c @@ -71,6 +71,7 @@ target_ulong HELPER(vsetvl)(CPURISCVState *env, target_ulong s1, env->vl = vl; env->vtype = s2; env->vstart = 0; + env->vill = 0; return vl; } From dceecac8a2fa36f6ab6927da2052f06e2de7a2a4 Mon Sep 17 00:00:00 2001 From: Anup Patel Date: Fri, 4 Feb 2022 23:16:37 +0530 Subject: [PATCH 437/460] target/riscv: Fix trap cause for RV32 HS-mode CSR access from RV64 HS-mode We should be returning illegal instruction trap when RV64 HS-mode tries to access RV32 HS-mode CSR. Fixes: d6f20dacea51 ("target/riscv: Fix 32-bit HS mode access permissions") Signed-off-by: Anup Patel Signed-off-by: Anup Patel Reviewed-by: Alistair Francis Reviewed-by: Bin Meng Reviewed-by: Frank Chang Message-id: 20220204174700.534953-2-anup@brainfault.org Signed-off-by: Alistair Francis --- target/riscv/csr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/riscv/csr.c b/target/riscv/csr.c index e5f9d4ef93..41a533a310 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -186,7 +186,7 @@ static RISCVException hmode(CPURISCVState *env, int csrno) static RISCVException hmode32(CPURISCVState *env, int csrno) { if (riscv_cpu_mxl(env) != MXL_RV32) { - if (riscv_cpu_virt_enabled(env)) { + if (!riscv_cpu_virt_enabled(env)) { return RISCV_EXCP_ILLEGAL_INST; } else { return RISCV_EXCP_VIRT_INSTRUCTION_FAULT; From 881df35d3df52efd845087fb76d0b0116b366468 Mon Sep 17 00:00:00 2001 From: Anup Patel Date: Fri, 4 Feb 2022 23:16:38 +0530 Subject: [PATCH 438/460] target/riscv: Implement SGEIP bit in hip and hie CSRs A hypervisor can optionally take guest external interrupts using SGEIP bit of hip and hie CSRs. Signed-off-by: Anup Patel Signed-off-by: Anup Patel Reviewed-by: Alistair Francis Reviewed-by: Frank Chang Message-id: 20220204174700.534953-3-anup@brainfault.org Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 3 ++- target/riscv/cpu_bits.h | 3 +++ target/riscv/csr.c | 18 +++++++++++------- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 1238aabe3f..e1224d26dc 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -434,6 +434,7 @@ static void riscv_cpu_reset(DeviceState *dev) } } env->mcause = 0; + env->miclaim = MIP_SGEIP; env->pc = env->resetvec; env->two_stage_lookup = false; /* mmte is supposed to have pm.current hardwired to 1 */ @@ -695,7 +696,7 @@ static void riscv_cpu_init(Object *obj) cpu_set_cpustate_pointers(cpu); #ifndef CONFIG_USER_ONLY - qdev_init_gpio_in(DEVICE(cpu), riscv_cpu_set_irq, 12); + qdev_init_gpio_in(DEVICE(cpu), riscv_cpu_set_irq, IRQ_LOCAL_MAX); #endif /* CONFIG_USER_ONLY */ } diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h index 7c87433645..e1256a9982 100644 --- a/target/riscv/cpu_bits.h +++ b/target/riscv/cpu_bits.h @@ -540,6 +540,8 @@ typedef enum RISCVException { #define IRQ_S_EXT 9 #define IRQ_VS_EXT 10 #define IRQ_M_EXT 11 +#define IRQ_S_GEXT 12 +#define IRQ_LOCAL_MAX 16 /* mip masks */ #define MIP_USIP (1 << IRQ_U_SOFT) @@ -554,6 +556,7 @@ typedef enum RISCVException { #define MIP_SEIP (1 << IRQ_S_EXT) #define MIP_VSEIP (1 << IRQ_VS_EXT) #define MIP_MEIP (1 << IRQ_M_EXT) +#define MIP_SGEIP (1 << IRQ_S_GEXT) /* sip masks */ #define SIP_SSIP MIP_SSIP diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 41a533a310..c635ffb089 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -461,12 +461,13 @@ static RISCVException read_timeh(CPURISCVState *env, int csrno, #define M_MODE_INTERRUPTS (MIP_MSIP | MIP_MTIP | MIP_MEIP) #define S_MODE_INTERRUPTS (MIP_SSIP | MIP_STIP | MIP_SEIP) #define VS_MODE_INTERRUPTS (MIP_VSSIP | MIP_VSTIP | MIP_VSEIP) +#define HS_MODE_INTERRUPTS (MIP_SGEIP | VS_MODE_INTERRUPTS) static const target_ulong delegable_ints = S_MODE_INTERRUPTS | VS_MODE_INTERRUPTS; static const target_ulong vs_delegable_ints = VS_MODE_INTERRUPTS; static const target_ulong all_ints = M_MODE_INTERRUPTS | S_MODE_INTERRUPTS | - VS_MODE_INTERRUPTS; + HS_MODE_INTERRUPTS; #define DELEGABLE_EXCPS ((1ULL << (RISCV_EXCP_INST_ADDR_MIS)) | \ (1ULL << (RISCV_EXCP_INST_ACCESS_FAULT)) | \ (1ULL << (RISCV_EXCP_ILLEGAL_INST)) | \ @@ -748,7 +749,7 @@ static RISCVException write_mideleg(CPURISCVState *env, int csrno, { env->mideleg = (env->mideleg & ~delegable_ints) | (val & delegable_ints); if (riscv_has_ext(env, RVH)) { - env->mideleg |= VS_MODE_INTERRUPTS; + env->mideleg |= HS_MODE_INTERRUPTS; } return RISCV_EXCP_NONE; } @@ -764,6 +765,9 @@ static RISCVException write_mie(CPURISCVState *env, int csrno, target_ulong val) { env->mie = (env->mie & ~all_ints) | (val & all_ints); + if (!riscv_has_ext(env, RVH)) { + env->mie &= ~MIP_SGEIP; + } return RISCV_EXCP_NONE; } @@ -1110,7 +1114,7 @@ static RISCVException rmw_sip(CPURISCVState *env, int csrno, } if (ret_value) { - *ret_value &= env->mideleg; + *ret_value &= env->mideleg & S_MODE_INTERRUPTS; } return ret; } @@ -1228,7 +1232,7 @@ static RISCVException rmw_hvip(CPURISCVState *env, int csrno, write_mask & hvip_writable_mask); if (ret_value) { - *ret_value &= hvip_writable_mask; + *ret_value &= VS_MODE_INTERRUPTS; } return ret; } @@ -1241,7 +1245,7 @@ static RISCVException rmw_hip(CPURISCVState *env, int csrno, write_mask & hip_writable_mask); if (ret_value) { - *ret_value &= hip_writable_mask; + *ret_value &= HS_MODE_INTERRUPTS; } return ret; } @@ -1249,14 +1253,14 @@ static RISCVException rmw_hip(CPURISCVState *env, int csrno, static RISCVException read_hie(CPURISCVState *env, int csrno, target_ulong *val) { - *val = env->mie & VS_MODE_INTERRUPTS; + *val = env->mie & HS_MODE_INTERRUPTS; return RISCV_EXCP_NONE; } static RISCVException write_hie(CPURISCVState *env, int csrno, target_ulong val) { - target_ulong newval = (env->mie & ~VS_MODE_INTERRUPTS) | (val & VS_MODE_INTERRUPTS); + target_ulong newval = (env->mie & ~HS_MODE_INTERRUPTS) | (val & HS_MODE_INTERRUPTS); return write_mie(env, CSR_MIE, newval); } From cd032fe75c1f7b24ccad772d50bfb689e7f5835d Mon Sep 17 00:00:00 2001 From: Anup Patel Date: Fri, 4 Feb 2022 23:16:39 +0530 Subject: [PATCH 439/460] target/riscv: Implement hgeie and hgeip CSRs The hgeie and hgeip CSRs are required for emulating an external interrupt controller capable of injecting virtual external interrupt to Guest/VM running at VS-level. Signed-off-by: Anup Patel Signed-off-by: Anup Patel Reviewed-by: Alistair Francis Reviewed-by: Frank Chang Message-id: 20220204174700.534953-4-anup@brainfault.org Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 67 +++++++++++++++++++++++++++------------ target/riscv/cpu.h | 5 +++ target/riscv/cpu_bits.h | 1 + target/riscv/cpu_helper.c | 37 +++++++++++++++++++-- target/riscv/csr.c | 43 +++++++++++++++++-------- target/riscv/machine.c | 6 ++-- 6 files changed, 121 insertions(+), 38 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index e1224d26dc..f1c268415a 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -663,27 +663,53 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp) static void riscv_cpu_set_irq(void *opaque, int irq, int level) { RISCVCPU *cpu = RISCV_CPU(opaque); + CPURISCVState *env = &cpu->env; - switch (irq) { - case IRQ_U_SOFT: - case IRQ_S_SOFT: - case IRQ_VS_SOFT: - case IRQ_M_SOFT: - case IRQ_U_TIMER: - case IRQ_S_TIMER: - case IRQ_VS_TIMER: - case IRQ_M_TIMER: - case IRQ_U_EXT: - case IRQ_S_EXT: - case IRQ_VS_EXT: - case IRQ_M_EXT: - if (kvm_enabled()) { - kvm_riscv_set_irq(cpu, irq, level); - } else { - riscv_cpu_update_mip(cpu, 1 << irq, BOOL_TO_MASK(level)); + if (irq < IRQ_LOCAL_MAX) { + switch (irq) { + case IRQ_U_SOFT: + case IRQ_S_SOFT: + case IRQ_VS_SOFT: + case IRQ_M_SOFT: + case IRQ_U_TIMER: + case IRQ_S_TIMER: + case IRQ_VS_TIMER: + case IRQ_M_TIMER: + case IRQ_U_EXT: + case IRQ_S_EXT: + case IRQ_VS_EXT: + case IRQ_M_EXT: + if (kvm_enabled()) { + kvm_riscv_set_irq(cpu, irq, level); + } else { + riscv_cpu_update_mip(cpu, 1 << irq, BOOL_TO_MASK(level)); + } + break; + default: + g_assert_not_reached(); } - break; - default: + } else if (irq < (IRQ_LOCAL_MAX + IRQ_LOCAL_GUEST_MAX)) { + /* Require H-extension for handling guest local interrupts */ + if (!riscv_has_ext(env, RVH)) { + g_assert_not_reached(); + } + + /* Compute bit position in HGEIP CSR */ + irq = irq - IRQ_LOCAL_MAX + 1; + if (env->geilen < irq) { + g_assert_not_reached(); + } + + /* Update HGEIP CSR */ + env->hgeip &= ~((target_ulong)1 << irq); + if (level) { + env->hgeip |= (target_ulong)1 << irq; + } + + /* Update mip.SGEIP bit */ + riscv_cpu_update_mip(cpu, MIP_SGEIP, + BOOL_TO_MASK(!!(env->hgeie & env->hgeip))); + } else { g_assert_not_reached(); } } @@ -696,7 +722,8 @@ static void riscv_cpu_init(Object *obj) cpu_set_cpustate_pointers(cpu); #ifndef CONFIG_USER_ONLY - qdev_init_gpio_in(DEVICE(cpu), riscv_cpu_set_irq, IRQ_LOCAL_MAX); + qdev_init_gpio_in(DEVICE(cpu), riscv_cpu_set_irq, + IRQ_LOCAL_MAX + IRQ_LOCAL_GUEST_MAX); #endif /* CONFIG_USER_ONLY */ } diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index aacc997d56..f030cb58b2 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -161,6 +161,7 @@ struct CPURISCVState { target_ulong priv; /* This contains QEMU specific information about the virt state. */ target_ulong virt; + target_ulong geilen; target_ulong resetvec; target_ulong mhartid; @@ -198,6 +199,8 @@ struct CPURISCVState { target_ulong htval; target_ulong htinst; target_ulong hgatp; + target_ulong hgeie; + target_ulong hgeip; uint64_t htimedelta; /* Upper 64-bits of 128-bit CSRs */ @@ -391,6 +394,8 @@ int riscv_cpu_write_elf32_note(WriteCoreDumpFunction f, CPUState *cs, int riscv_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); int riscv_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); bool riscv_cpu_fp_enabled(CPURISCVState *env); +target_ulong riscv_cpu_get_geilen(CPURISCVState *env); +void riscv_cpu_set_geilen(CPURISCVState *env, target_ulong geilen); bool riscv_cpu_vector_enabled(CPURISCVState *env); bool riscv_cpu_virt_enabled(CPURISCVState *env); void riscv_cpu_set_virt_enabled(CPURISCVState *env, bool enable); diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h index e1256a9982..a541705760 100644 --- a/target/riscv/cpu_bits.h +++ b/target/riscv/cpu_bits.h @@ -542,6 +542,7 @@ typedef enum RISCVException { #define IRQ_M_EXT 11 #define IRQ_S_GEXT 12 #define IRQ_LOCAL_MAX 16 +#define IRQ_LOCAL_GUEST_MAX (TARGET_LONG_BITS - 1) /* mip masks */ #define MIP_USIP (1 << IRQ_U_SOFT) diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 327a2c4f1d..698389ba1b 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -159,7 +159,11 @@ static int riscv_cpu_local_irq_pending(CPURISCVState *env) target_ulong mstatus_mie = get_field(env->mstatus, MSTATUS_MIE); target_ulong mstatus_sie = get_field(env->mstatus, MSTATUS_SIE); - target_ulong pending = env->mip & env->mie; + target_ulong vsgemask = + (target_ulong)1 << get_field(env->hstatus, HSTATUS_VGEIN); + target_ulong vsgein = (env->hgeip & vsgemask) ? MIP_VSEIP : 0; + + target_ulong pending = (env->mip | vsgein) & env->mie; target_ulong mie = env->priv < PRV_M || (env->priv == PRV_M && mstatus_mie); @@ -279,6 +283,28 @@ void riscv_cpu_swap_hypervisor_regs(CPURISCVState *env) } } +target_ulong riscv_cpu_get_geilen(CPURISCVState *env) +{ + if (!riscv_has_ext(env, RVH)) { + return 0; + } + + return env->geilen; +} + +void riscv_cpu_set_geilen(CPURISCVState *env, target_ulong geilen) +{ + if (!riscv_has_ext(env, RVH)) { + return; + } + + if (geilen > (TARGET_LONG_BITS - 1)) { + return; + } + + env->geilen = geilen; +} + bool riscv_cpu_virt_enabled(CPURISCVState *env) { if (!riscv_has_ext(env, RVH)) { @@ -322,9 +348,14 @@ uint32_t riscv_cpu_update_mip(RISCVCPU *cpu, uint32_t mask, uint32_t value) { CPURISCVState *env = &cpu->env; CPUState *cs = CPU(cpu); - uint32_t old = env->mip; + uint32_t gein, vsgein = 0, old = env->mip; bool locked = false; + if (riscv_cpu_virt_enabled(env)) { + gein = get_field(env->hstatus, HSTATUS_VGEIN); + vsgein = (env->hgeip & (1ULL << gein)) ? MIP_VSEIP : 0; + } + if (!qemu_mutex_iothread_locked()) { locked = true; qemu_mutex_lock_iothread(); @@ -332,7 +363,7 @@ uint32_t riscv_cpu_update_mip(RISCVCPU *cpu, uint32_t mask, uint32_t value) env->mip = (env->mip & ~mask) | (value & mask); - if (env->mip) { + if (env->mip | vsgein) { cpu_interrupt(cs, CPU_INTERRUPT_HARD); } else { cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); diff --git a/target/riscv/csr.c b/target/riscv/csr.c index c635ffb089..b23195b479 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -883,7 +883,7 @@ static RISCVException rmw_mip(CPURISCVState *env, int csrno, RISCVCPU *cpu = env_archcpu(env); /* Allow software control of delegable interrupts not claimed by hardware */ target_ulong mask = write_mask & delegable_ints & ~env->miclaim; - uint32_t old_mip; + uint32_t gin, old_mip; if (mask) { old_mip = riscv_cpu_update_mip(cpu, mask, (new_value & mask)); @@ -891,6 +891,11 @@ static RISCVException rmw_mip(CPURISCVState *env, int csrno, old_mip = env->mip; } + if (csrno != CSR_HVIP) { + gin = get_field(env->hstatus, HSTATUS_VGEIN); + old_mip |= (env->hgeip & ((target_ulong)1 << gin)) ? MIP_VSEIP : 0; + } + if (ret_value) { *ret_value = old_mip; } @@ -1089,7 +1094,7 @@ static RISCVException rmw_vsip(CPURISCVState *env, int csrno, target_ulong new_value, target_ulong write_mask) { /* Shift the S bits to their VS bit location in mip */ - int ret = rmw_mip(env, 0, ret_value, new_value << 1, + int ret = rmw_mip(env, csrno, ret_value, new_value << 1, (write_mask << 1) & vsip_writable_mask & env->hideleg); if (ret_value) { @@ -1109,7 +1114,7 @@ static RISCVException rmw_sip(CPURISCVState *env, int csrno, if (riscv_cpu_virt_enabled(env)) { ret = rmw_vsip(env, CSR_VSIP, ret_value, new_value, write_mask); } else { - ret = rmw_mip(env, CSR_MSTATUS, ret_value, new_value, + ret = rmw_mip(env, csrno, ret_value, new_value, write_mask & env->mideleg & sip_writable_mask); } @@ -1228,7 +1233,7 @@ static RISCVException rmw_hvip(CPURISCVState *env, int csrno, target_ulong *ret_value, target_ulong new_value, target_ulong write_mask) { - int ret = rmw_mip(env, 0, ret_value, new_value, + int ret = rmw_mip(env, csrno, ret_value, new_value, write_mask & hvip_writable_mask); if (ret_value) { @@ -1241,7 +1246,7 @@ static RISCVException rmw_hip(CPURISCVState *env, int csrno, target_ulong *ret_value, target_ulong new_value, target_ulong write_mask) { - int ret = rmw_mip(env, 0, ret_value, new_value, + int ret = rmw_mip(env, csrno, ret_value, new_value, write_mask & hip_writable_mask); if (ret_value) { @@ -1278,12 +1283,24 @@ static RISCVException write_hcounteren(CPURISCVState *env, int csrno, return RISCV_EXCP_NONE; } +static RISCVException read_hgeie(CPURISCVState *env, int csrno, + target_ulong *val) +{ + if (val) { + *val = env->hgeie; + } + return RISCV_EXCP_NONE; +} + static RISCVException write_hgeie(CPURISCVState *env, int csrno, target_ulong val) { - if (val) { - qemu_log_mask(LOG_UNIMP, "No support for a non-zero GEILEN."); - } + /* Only GEILEN:1 bits implemented and BIT0 is never implemented */ + val &= ((((target_ulong)1) << env->geilen) - 1) << 1; + env->hgeie = val; + /* Update mip.SGEIP bit */ + riscv_cpu_update_mip(env_archcpu(env), MIP_SGEIP, + BOOL_TO_MASK(!!(env->hgeie & env->hgeip))); return RISCV_EXCP_NONE; } @@ -1314,11 +1331,11 @@ static RISCVException write_htinst(CPURISCVState *env, int csrno, return RISCV_EXCP_NONE; } -static RISCVException write_hgeip(CPURISCVState *env, int csrno, - target_ulong val) +static RISCVException read_hgeip(CPURISCVState *env, int csrno, + target_ulong *val) { if (val) { - qemu_log_mask(LOG_UNIMP, "No support for a non-zero GEILEN."); + *val = env->hgeip; } return RISCV_EXCP_NONE; } @@ -2148,10 +2165,10 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { [CSR_HIP] = { "hip", hmode, NULL, NULL, rmw_hip }, [CSR_HIE] = { "hie", hmode, read_hie, write_hie }, [CSR_HCOUNTEREN] = { "hcounteren", hmode, read_hcounteren, write_hcounteren }, - [CSR_HGEIE] = { "hgeie", hmode, read_zero, write_hgeie }, + [CSR_HGEIE] = { "hgeie", hmode, read_hgeie, write_hgeie }, [CSR_HTVAL] = { "htval", hmode, read_htval, write_htval }, [CSR_HTINST] = { "htinst", hmode, read_htinst, write_htinst }, - [CSR_HGEIP] = { "hgeip", hmode, read_zero, write_hgeip }, + [CSR_HGEIP] = { "hgeip", hmode, read_hgeip, NULL }, [CSR_HGATP] = { "hgatp", hmode, read_hgatp, write_hgatp }, [CSR_HTIMEDELTA] = { "htimedelta", hmode, read_htimedelta, write_htimedelta }, [CSR_HTIMEDELTAH] = { "htimedeltah", hmode32, read_htimedeltah, write_htimedeltah }, diff --git a/target/riscv/machine.c b/target/riscv/machine.c index 740e11fcff..7d72c2d8a6 100644 --- a/target/riscv/machine.c +++ b/target/riscv/machine.c @@ -78,8 +78,8 @@ static bool hyper_needed(void *opaque) static const VMStateDescription vmstate_hyper = { .name = "cpu/hyper", - .version_id = 1, - .minimum_version_id = 1, + .version_id = 2, + .minimum_version_id = 2, .needed = hyper_needed, .fields = (VMStateField[]) { VMSTATE_UINTTL(env.hstatus, RISCVCPU), @@ -89,6 +89,8 @@ static const VMStateDescription vmstate_hyper = { VMSTATE_UINTTL(env.htval, RISCVCPU), VMSTATE_UINTTL(env.htinst, RISCVCPU), VMSTATE_UINTTL(env.hgatp, RISCVCPU), + VMSTATE_UINTTL(env.hgeie, RISCVCPU), + VMSTATE_UINTTL(env.hgeip, RISCVCPU), VMSTATE_UINT64(env.htimedelta, RISCVCPU), VMSTATE_UINT64(env.vsstatus, RISCVCPU), From 02d9565b92c97af6bac2ff1bb18967a5e95b9694 Mon Sep 17 00:00:00 2001 From: Anup Patel Date: Fri, 4 Feb 2022 23:16:40 +0530 Subject: [PATCH 440/460] target/riscv: Improve delivery of guest external interrupts 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. Signed-off-by: Anup Patel Signed-off-by: Anup Patel Reviewed-by: Alistair Francis Reviewed-by: Frank Chang Message-id: 20220204174700.534953-5-anup@brainfault.org Signed-off-by: Alistair Francis --- target/riscv/cpu_helper.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 698389ba1b..e45ca08ea9 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -326,6 +326,19 @@ void riscv_cpu_set_virt_enabled(CPURISCVState *env, bool enable) } env->virt = set_field(env->virt, VIRT_ONOFF, 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_archcpu(env), 0, 0); + } } bool riscv_cpu_two_stage_lookup(int mmu_idx) From f87adf23fa66fd07d9f003173d386c0a54d9ddb0 Mon Sep 17 00:00:00 2001 From: Anup Patel Date: Fri, 4 Feb 2022 23:16:41 +0530 Subject: [PATCH 441/460] target/riscv: Allow setting CPU feature from machine/device emulation The machine or device emulation should be able to force set certain CPU features because: 1) We can have certain CPU features which are in-general optional but implemented by RISC-V CPUs on the machine. 2) We can have devices which require a certain CPU feature. For example, AIA IMSIC devices expect AIA CSRs implemented by RISC-V CPUs. Signed-off-by: Anup Patel Signed-off-by: Anup Patel Reviewed-by: Bin Meng Reviewed-by: Alistair Francis Reviewed-by: Frank Chang Message-id: 20220204174700.534953-6-anup@brainfault.org Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 11 +++-------- target/riscv/cpu.h | 5 +++++ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index f1c268415a..ff766acc21 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -135,11 +135,6 @@ static void set_vext_version(CPURISCVState *env, int vext_ver) env->vext_ver = vext_ver; } -static void set_feature(CPURISCVState *env, int feature) -{ - env->features |= (1ULL << feature); -} - static void set_resetvec(CPURISCVState *env, target_ulong resetvec) { #ifndef CONFIG_USER_ONLY @@ -508,18 +503,18 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp) } if (cpu->cfg.mmu) { - set_feature(env, RISCV_FEATURE_MMU); + riscv_set_feature(env, RISCV_FEATURE_MMU); } if (cpu->cfg.pmp) { - set_feature(env, RISCV_FEATURE_PMP); + riscv_set_feature(env, RISCV_FEATURE_PMP); /* * Enhanced PMP should only be available * on harts with PMP support */ if (cpu->cfg.epmp) { - set_feature(env, RISCV_FEATURE_EPMP); + riscv_set_feature(env, RISCV_FEATURE_EPMP); } } diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index f030cb58b2..283a3cda4b 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -379,6 +379,11 @@ static inline bool riscv_feature(CPURISCVState *env, int feature) return env->features & (1ULL << feature); } +static inline void riscv_set_feature(CPURISCVState *env, int feature) +{ + env->features |= (1ULL << feature); +} + #include "cpu_user.h" extern const char * const riscv_int_regnames[]; From 32b0ada038629311aa90499a68de29473df7935d Mon Sep 17 00:00:00 2001 From: Anup Patel Date: Fri, 4 Feb 2022 23:16:42 +0530 Subject: [PATCH 442/460] target/riscv: Add AIA cpu feature We define a CPU feature for AIA CSR support in RISC-V CPUs which can be set by machine/device emulation. The RISC-V CSR emulation will also check this feature for emulating AIA CSRs. Signed-off-by: Anup Patel Signed-off-by: Anup Patel Reviewed-by: Bin Meng Reviewed-by: Alistair Francis Reviewed-by: Frank Chang Message-id: 20220204174700.534953-7-anup@brainfault.org Signed-off-by: Alistair Francis --- target/riscv/cpu.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 283a3cda4b..8838c61ae4 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -78,7 +78,8 @@ enum { RISCV_FEATURE_MMU, RISCV_FEATURE_PMP, RISCV_FEATURE_EPMP, - RISCV_FEATURE_MISA + RISCV_FEATURE_MISA, + RISCV_FEATURE_AIA }; #define PRIV_VERSION_1_10_0 0x00011000 From aa7508bbc63afe5c9fb65ce3353c9828ee12c4b3 Mon Sep 17 00:00:00 2001 From: Anup Patel Date: Fri, 4 Feb 2022 23:16:43 +0530 Subject: [PATCH 443/460] target/riscv: Add defines for AIA CSRs The RISC-V AIA specification extends RISC-V local interrupts and introduces new CSRs. This patch adds defines for the new AIA CSRs. Signed-off-by: Anup Patel Signed-off-by: Anup Patel Reviewed-by: Alistair Francis Reviewed-by: Frank Chang Message-id: 20220204174700.534953-8-anup@brainfault.org Signed-off-by: Alistair Francis --- target/riscv/cpu_bits.h | 119 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 119 insertions(+) diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h index a541705760..068c4d8034 100644 --- a/target/riscv/cpu_bits.h +++ b/target/riscv/cpu_bits.h @@ -168,6 +168,31 @@ #define CSR_MTVAL 0x343 #define CSR_MIP 0x344 +/* Machine-Level Window to Indirectly Accessed Registers (AIA) */ +#define CSR_MISELECT 0x350 +#define CSR_MIREG 0x351 + +/* Machine-Level Interrupts (AIA) */ +#define CSR_MTOPI 0xfb0 + +/* Machine-Level IMSIC Interface (AIA) */ +#define CSR_MSETEIPNUM 0x358 +#define CSR_MCLREIPNUM 0x359 +#define CSR_MSETEIENUM 0x35a +#define CSR_MCLREIENUM 0x35b +#define CSR_MTOPEI 0x35c + +/* Virtual Interrupts for Supervisor Level (AIA) */ +#define CSR_MVIEN 0x308 +#define CSR_MVIP 0x309 + +/* Machine-Level High-Half CSRs (AIA) */ +#define CSR_MIDELEGH 0x313 +#define CSR_MIEH 0x314 +#define CSR_MVIENH 0x318 +#define CSR_MVIPH 0x319 +#define CSR_MIPH 0x354 + /* Supervisor Trap Setup */ #define CSR_SSTATUS 0x100 #define CSR_SEDELEG 0x102 @@ -187,6 +212,24 @@ #define CSR_SPTBR 0x180 #define CSR_SATP 0x180 +/* Supervisor-Level Window to Indirectly Accessed Registers (AIA) */ +#define CSR_SISELECT 0x150 +#define CSR_SIREG 0x151 + +/* Supervisor-Level Interrupts (AIA) */ +#define CSR_STOPI 0xdb0 + +/* Supervisor-Level IMSIC Interface (AIA) */ +#define CSR_SSETEIPNUM 0x158 +#define CSR_SCLREIPNUM 0x159 +#define CSR_SSETEIENUM 0x15a +#define CSR_SCLREIENUM 0x15b +#define CSR_STOPEI 0x15c + +/* Supervisor-Level High-Half CSRs (AIA) */ +#define CSR_SIEH 0x114 +#define CSR_SIPH 0x154 + /* Hpervisor CSRs */ #define CSR_HSTATUS 0x600 #define CSR_HEDELEG 0x602 @@ -217,6 +260,35 @@ #define CSR_MTINST 0x34a #define CSR_MTVAL2 0x34b +/* Virtual Interrupts and Interrupt Priorities (H-extension with AIA) */ +#define CSR_HVIEN 0x608 +#define CSR_HVICTL 0x609 +#define CSR_HVIPRIO1 0x646 +#define CSR_HVIPRIO2 0x647 + +/* VS-Level Window to Indirectly Accessed Registers (H-extension with AIA) */ +#define CSR_VSISELECT 0x250 +#define CSR_VSIREG 0x251 + +/* VS-Level Interrupts (H-extension with AIA) */ +#define CSR_VSTOPI 0xeb0 + +/* VS-Level IMSIC Interface (H-extension with AIA) */ +#define CSR_VSSETEIPNUM 0x258 +#define CSR_VSCLREIPNUM 0x259 +#define CSR_VSSETEIENUM 0x25a +#define CSR_VSCLREIENUM 0x25b +#define CSR_VSTOPEI 0x25c + +/* Hypervisor and VS-Level High-Half CSRs (H-extension with AIA) */ +#define CSR_HIDELEGH 0x613 +#define CSR_HVIENH 0x618 +#define CSR_HVIPH 0x655 +#define CSR_HVIPRIO1H 0x656 +#define CSR_HVIPRIO2H 0x657 +#define CSR_VSIEH 0x214 +#define CSR_VSIPH 0x254 + /* Enhanced Physical Memory Protection (ePMP) */ #define CSR_MSECCFG 0x747 #define CSR_MSECCFGH 0x757 @@ -635,4 +707,51 @@ typedef enum RISCVException { #define UMTE_U_PM_INSN U_PM_INSN #define UMTE_MASK (UMTE_U_PM_ENABLE | MMTE_U_PM_CURRENT | UMTE_U_PM_INSN) +/* MISELECT, SISELECT, and VSISELECT bits (AIA) */ +#define ISELECT_IPRIO0 0x30 +#define ISELECT_IPRIO15 0x3f +#define ISELECT_IMSIC_EIDELIVERY 0x70 +#define ISELECT_IMSIC_EITHRESHOLD 0x72 +#define ISELECT_IMSIC_EIP0 0x80 +#define ISELECT_IMSIC_EIP63 0xbf +#define ISELECT_IMSIC_EIE0 0xc0 +#define ISELECT_IMSIC_EIE63 0xff +#define ISELECT_IMSIC_FIRST ISELECT_IMSIC_EIDELIVERY +#define ISELECT_IMSIC_LAST ISELECT_IMSIC_EIE63 +#define ISELECT_MASK 0x1ff + +/* Dummy [M|S|VS]ISELECT value for emulating [M|S|VS]TOPEI CSRs */ +#define ISELECT_IMSIC_TOPEI (ISELECT_MASK + 1) + +/* IMSIC bits (AIA) */ +#define IMSIC_TOPEI_IID_SHIFT 16 +#define IMSIC_TOPEI_IID_MASK 0x7ff +#define IMSIC_TOPEI_IPRIO_MASK 0x7ff +#define IMSIC_EIPx_BITS 32 +#define IMSIC_EIEx_BITS 32 + +/* MTOPI and STOPI bits (AIA) */ +#define TOPI_IID_SHIFT 16 +#define TOPI_IID_MASK 0xfff +#define TOPI_IPRIO_MASK 0xff + +/* Interrupt priority bits (AIA) */ +#define IPRIO_IRQ_BITS 8 +#define IPRIO_MMAXIPRIO 255 +#define IPRIO_DEFAULT_UPPER 4 +#define IPRIO_DEFAULT_MIDDLE (IPRIO_DEFAULT_UPPER + 24) +#define IPRIO_DEFAULT_M IPRIO_DEFAULT_MIDDLE +#define IPRIO_DEFAULT_S (IPRIO_DEFAULT_M + 3) +#define IPRIO_DEFAULT_SGEXT (IPRIO_DEFAULT_S + 3) +#define IPRIO_DEFAULT_VS (IPRIO_DEFAULT_SGEXT + 1) +#define IPRIO_DEFAULT_LOWER (IPRIO_DEFAULT_VS + 3) + +/* HVICTL bits (AIA) */ +#define HVICTL_VTI 0x40000000 +#define HVICTL_IID 0x0fff0000 +#define HVICTL_IPRIOM 0x00000100 +#define HVICTL_IPRIO 0x000000ff +#define HVICTL_VALID_MASK \ + (HVICTL_VTI | HVICTL_IID | HVICTL_IPRIOM | HVICTL_IPRIO) + #endif From 69077dd687a5e388943548b0eb8e3747cc047324 Mon Sep 17 00:00:00 2001 From: Anup Patel Date: Fri, 4 Feb 2022 23:16:44 +0530 Subject: [PATCH 444/460] target/riscv: Allow AIA device emulation to set ireg rmw callback The AIA device emulation (such as AIA IMSIC) should be able to set (or provide) AIA ireg read-modify-write callback for each privilege level of a RISC-V HART. Signed-off-by: Anup Patel Signed-off-by: Anup Patel Reviewed-by: Alistair Francis Reviewed-by: Frank Chang Message-id: 20220204174700.534953-9-anup@brainfault.org Signed-off-by: Alistair Francis --- target/riscv/cpu.h | 23 +++++++++++++++++++++++ target/riscv/cpu_helper.c | 14 ++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 8838c61ae4..6b6df57c42 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -256,6 +256,22 @@ struct CPURISCVState { uint64_t (*rdtime_fn)(uint32_t); uint32_t rdtime_fn_arg; + /* machine specific AIA ireg read-modify-write callback */ +#define AIA_MAKE_IREG(__isel, __priv, __virt, __vgein, __xlen) \ + ((((__xlen) & 0xff) << 24) | \ + (((__vgein) & 0x3f) << 20) | \ + (((__virt) & 0x1) << 18) | \ + (((__priv) & 0x3) << 16) | \ + (__isel & 0xffff)) +#define AIA_IREG_ISEL(__ireg) ((__ireg) & 0xffff) +#define AIA_IREG_PRIV(__ireg) (((__ireg) >> 16) & 0x3) +#define AIA_IREG_VIRT(__ireg) (((__ireg) >> 18) & 0x1) +#define AIA_IREG_VGEIN(__ireg) (((__ireg) >> 20) & 0x3f) +#define AIA_IREG_XLEN(__ireg) (((__ireg) >> 24) & 0xff) + int (*aia_ireg_rmw_fn[4])(void *arg, target_ulong reg, + target_ulong *val, target_ulong new_val, target_ulong write_mask); + void *aia_ireg_rmw_fn_arg[4]; + /* True if in debugger mode. */ bool debugger; @@ -433,6 +449,13 @@ uint32_t riscv_cpu_update_mip(RISCVCPU *cpu, uint32_t mask, uint32_t value); #define BOOL_TO_MASK(x) (-!!(x)) /* helper for riscv_cpu_update_mip value */ void riscv_cpu_set_rdtime_fn(CPURISCVState *env, uint64_t (*fn)(uint32_t), uint32_t arg); +void riscv_cpu_set_aia_ireg_rmw_fn(CPURISCVState *env, uint32_t priv, + int (*rmw_fn)(void *arg, + target_ulong reg, + target_ulong *val, + target_ulong new_val, + target_ulong write_mask), + void *rmw_fn_arg); #endif void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv); diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index e45ca08ea9..37c58a891b 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -396,6 +396,20 @@ void riscv_cpu_set_rdtime_fn(CPURISCVState *env, uint64_t (*fn)(uint32_t), env->rdtime_fn_arg = arg; } +void riscv_cpu_set_aia_ireg_rmw_fn(CPURISCVState *env, uint32_t priv, + int (*rmw_fn)(void *arg, + target_ulong reg, + target_ulong *val, + target_ulong new_val, + target_ulong write_mask), + void *rmw_fn_arg) +{ + if (priv <= PRV_M) { + env->aia_ireg_rmw_fn[priv] = rmw_fn; + env->aia_ireg_rmw_fn_arg[priv] = rmw_fn_arg; + } +} + void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv) { if (newpriv > PRV_M) { From 43dc93af36dced9d23911be2ed6b0fe82bf3c42c Mon Sep 17 00:00:00 2001 From: Anup Patel Date: Fri, 4 Feb 2022 23:16:45 +0530 Subject: [PATCH 445/460] target/riscv: Implement AIA local interrupt priorities The AIA spec defines programmable 8-bit priority for each local interrupt at M-level, S-level and VS-level so we extend local interrupt processing to consider AIA interrupt priorities. The AIA CSRs which help software configure local interrupt priorities will be added by subsequent patches. Signed-off-by: Anup Patel Signed-off-by: Anup Patel Reviewed-by: Alistair Francis Message-id: 20220204174700.534953-10-anup@brainfault.org Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 19 +++ target/riscv/cpu.h | 12 ++ target/riscv/cpu_helper.c | 289 ++++++++++++++++++++++++++++++++++---- target/riscv/machine.c | 3 + 4 files changed, 298 insertions(+), 25 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index ff766acc21..5fb0a61036 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -400,6 +400,10 @@ void restore_state_to_opc(CPURISCVState *env, TranslationBlock *tb, static void riscv_cpu_reset(DeviceState *dev) { +#ifndef CONFIG_USER_ONLY + uint8_t iprio; + int i, irq, rdzero; +#endif CPUState *cs = CPU(dev); RISCVCPU *cpu = RISCV_CPU(cs); RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(cpu); @@ -432,6 +436,21 @@ static void riscv_cpu_reset(DeviceState *dev) env->miclaim = MIP_SGEIP; env->pc = env->resetvec; env->two_stage_lookup = false; + + /* Initialized default priorities of local interrupts. */ + for (i = 0; i < ARRAY_SIZE(env->miprio); i++) { + iprio = riscv_cpu_default_priority(i); + env->miprio[i] = (i == IRQ_M_EXT) ? 0 : iprio; + env->siprio[i] = (i == IRQ_S_EXT) ? 0 : iprio; + env->hviprio[i] = 0; + } + i = 0; + while (!riscv_cpu_hviprio_index2irq(i, &irq, &rdzero)) { + if (!rdzero) { + env->hviprio[irq] = env->miprio[irq]; + } + i++; + } /* mmte is supposed to have pm.current hardwired to 1 */ env->mmte |= (PM_EXT_INITIAL | MMTE_M_PM_CURRENT); #endif diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 6b6df57c42..89e9cc558d 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -192,6 +192,10 @@ struct CPURISCVState { target_ulong mcause; target_ulong mtval; /* since: priv-1.10.0 */ + /* Machine and Supervisor interrupt priorities */ + uint8_t miprio[64]; + uint8_t siprio[64]; + /* Hypervisor CSRs */ target_ulong hstatus; target_ulong hedeleg; @@ -204,6 +208,9 @@ struct CPURISCVState { target_ulong hgeip; uint64_t htimedelta; + /* Hypervisor controlled virtual interrupt priorities */ + uint8_t hviprio[64]; + /* Upper 64-bits of 128-bit CSRs */ uint64_t mscratchh; uint64_t sscratchh; @@ -415,6 +422,11 @@ int riscv_cpu_write_elf32_note(WriteCoreDumpFunction f, CPUState *cs, int cpuid, void *opaque); int riscv_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); int riscv_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); +int riscv_cpu_hviprio_index2irq(int index, int *out_irq, int *out_rdzero); +uint8_t riscv_cpu_default_priority(int irq); +int riscv_cpu_mirq_pending(CPURISCVState *env); +int riscv_cpu_sirq_pending(CPURISCVState *env); +int riscv_cpu_vsirq_pending(CPURISCVState *env); bool riscv_cpu_fp_enabled(CPURISCVState *env); target_ulong riscv_cpu_get_geilen(CPURISCVState *env); void riscv_cpu_set_geilen(CPURISCVState *env, target_ulong geilen); diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 37c58a891b..1a9534d6d7 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -152,36 +152,275 @@ void riscv_cpu_update_mask(CPURISCVState *env) } #ifndef CONFIG_USER_ONLY + +/* + * The HS-mode is allowed to configure priority only for the + * following VS-mode local interrupts: + * + * 0 (Reserved interrupt, reads as zero) + * 1 Supervisor software interrupt + * 4 (Reserved interrupt, reads as zero) + * 5 Supervisor timer interrupt + * 8 (Reserved interrupt, reads as zero) + * 13 (Reserved interrupt) + * 14 " + * 15 " + * 16 " + * 18 Debug/trace interrupt + * 20 (Reserved interrupt) + * 22 " + * 24 " + * 26 " + * 28 " + * 30 (Reserved for standard reporting of bus or system errors) + */ + +static const int hviprio_index2irq[] = { + 0, 1, 4, 5, 8, 13, 14, 15, 16, 18, 20, 22, 24, 26, 28, 30 }; +static const int hviprio_index2rdzero[] = { + 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + +int riscv_cpu_hviprio_index2irq(int index, int *out_irq, int *out_rdzero) +{ + if (index < 0 || ARRAY_SIZE(hviprio_index2irq) <= index) { + return -EINVAL; + } + + if (out_irq) { + *out_irq = hviprio_index2irq[index]; + } + + if (out_rdzero) { + *out_rdzero = hviprio_index2rdzero[index]; + } + + return 0; +} + +/* + * Default priorities of local interrupts are defined in the + * RISC-V Advanced Interrupt Architecture specification. + * + * ---------------------------------------------------------------- + * Default | + * Priority | Major Interrupt Numbers + * ---------------------------------------------------------------- + * Highest | 63 (3f), 62 (3e), 31 (1f), 30 (1e), 61 (3d), 60 (3c), + * | 59 (3b), 58 (3a), 29 (1d), 28 (1c), 57 (39), 56 (38), + * | 55 (37), 54 (36), 27 (1b), 26 (1a), 53 (35), 52 (34), + * | 51 (33), 50 (32), 25 (19), 24 (18), 49 (31), 48 (30) + * | + * | 11 (0b), 3 (03), 7 (07) + * | 9 (09), 1 (01), 5 (05) + * | 12 (0c) + * | 10 (0a), 2 (02), 6 (06) + * | + * | 47 (2f), 46 (2e), 23 (17), 22 (16), 45 (2d), 44 (2c), + * | 43 (2b), 42 (2a), 21 (15), 20 (14), 41 (29), 40 (28), + * | 39 (27), 38 (26), 19 (13), 18 (12), 37 (25), 36 (24), + * Lowest | 35 (23), 34 (22), 17 (11), 16 (10), 33 (21), 32 (20) + * ---------------------------------------------------------------- + */ +static const uint8_t default_iprio[64] = { + [63] = IPRIO_DEFAULT_UPPER, + [62] = IPRIO_DEFAULT_UPPER + 1, + [31] = IPRIO_DEFAULT_UPPER + 2, + [30] = IPRIO_DEFAULT_UPPER + 3, + [61] = IPRIO_DEFAULT_UPPER + 4, + [60] = IPRIO_DEFAULT_UPPER + 5, + + [59] = IPRIO_DEFAULT_UPPER + 6, + [58] = IPRIO_DEFAULT_UPPER + 7, + [29] = IPRIO_DEFAULT_UPPER + 8, + [28] = IPRIO_DEFAULT_UPPER + 9, + [57] = IPRIO_DEFAULT_UPPER + 10, + [56] = IPRIO_DEFAULT_UPPER + 11, + + [55] = IPRIO_DEFAULT_UPPER + 12, + [54] = IPRIO_DEFAULT_UPPER + 13, + [27] = IPRIO_DEFAULT_UPPER + 14, + [26] = IPRIO_DEFAULT_UPPER + 15, + [53] = IPRIO_DEFAULT_UPPER + 16, + [52] = IPRIO_DEFAULT_UPPER + 17, + + [51] = IPRIO_DEFAULT_UPPER + 18, + [50] = IPRIO_DEFAULT_UPPER + 19, + [25] = IPRIO_DEFAULT_UPPER + 20, + [24] = IPRIO_DEFAULT_UPPER + 21, + [49] = IPRIO_DEFAULT_UPPER + 22, + [48] = IPRIO_DEFAULT_UPPER + 23, + + [11] = IPRIO_DEFAULT_M, + [3] = IPRIO_DEFAULT_M + 1, + [7] = IPRIO_DEFAULT_M + 2, + + [9] = IPRIO_DEFAULT_S, + [1] = IPRIO_DEFAULT_S + 1, + [5] = IPRIO_DEFAULT_S + 2, + + [12] = IPRIO_DEFAULT_SGEXT, + + [10] = IPRIO_DEFAULT_VS, + [2] = IPRIO_DEFAULT_VS + 1, + [6] = IPRIO_DEFAULT_VS + 2, + + [47] = IPRIO_DEFAULT_LOWER, + [46] = IPRIO_DEFAULT_LOWER + 1, + [23] = IPRIO_DEFAULT_LOWER + 2, + [22] = IPRIO_DEFAULT_LOWER + 3, + [45] = IPRIO_DEFAULT_LOWER + 4, + [44] = IPRIO_DEFAULT_LOWER + 5, + + [43] = IPRIO_DEFAULT_LOWER + 6, + [42] = IPRIO_DEFAULT_LOWER + 7, + [21] = IPRIO_DEFAULT_LOWER + 8, + [20] = IPRIO_DEFAULT_LOWER + 9, + [41] = IPRIO_DEFAULT_LOWER + 10, + [40] = IPRIO_DEFAULT_LOWER + 11, + + [39] = IPRIO_DEFAULT_LOWER + 12, + [38] = IPRIO_DEFAULT_LOWER + 13, + [19] = IPRIO_DEFAULT_LOWER + 14, + [18] = IPRIO_DEFAULT_LOWER + 15, + [37] = IPRIO_DEFAULT_LOWER + 16, + [36] = IPRIO_DEFAULT_LOWER + 17, + + [35] = IPRIO_DEFAULT_LOWER + 18, + [34] = IPRIO_DEFAULT_LOWER + 19, + [17] = IPRIO_DEFAULT_LOWER + 20, + [16] = IPRIO_DEFAULT_LOWER + 21, + [33] = IPRIO_DEFAULT_LOWER + 22, + [32] = IPRIO_DEFAULT_LOWER + 23, +}; + +uint8_t riscv_cpu_default_priority(int irq) +{ + if (irq < 0 || irq > 63) { + return IPRIO_MMAXIPRIO; + } + + return default_iprio[irq] ? default_iprio[irq] : IPRIO_MMAXIPRIO; +}; + +static int riscv_cpu_pending_to_irq(CPURISCVState *env, + int extirq, unsigned int extirq_def_prio, + uint64_t pending, uint8_t *iprio) +{ + int irq, best_irq = RISCV_EXCP_NONE; + unsigned int prio, best_prio = UINT_MAX; + + if (!pending) { + return RISCV_EXCP_NONE; + } + + irq = ctz64(pending); + if (!riscv_feature(env, RISCV_FEATURE_AIA)) { + return irq; + } + + pending = pending >> irq; + while (pending) { + prio = iprio[irq]; + if (!prio) { + if (irq == extirq) { + prio = extirq_def_prio; + } else { + prio = (riscv_cpu_default_priority(irq) < extirq_def_prio) ? + 1 : IPRIO_MMAXIPRIO; + } + } + if ((pending & 0x1) && (prio <= best_prio)) { + best_irq = irq; + best_prio = prio; + } + irq++; + pending = pending >> 1; + } + + return best_irq; +} + +static uint64_t riscv_cpu_all_pending(CPURISCVState *env) +{ + uint32_t gein = get_field(env->hstatus, HSTATUS_VGEIN); + uint64_t vsgein = (env->hgeip & (1ULL << gein)) ? MIP_VSEIP : 0; + + return (env->mip | vsgein) & env->mie; +} + +int riscv_cpu_mirq_pending(CPURISCVState *env) +{ + uint64_t irqs = riscv_cpu_all_pending(env) & ~env->mideleg & + ~(MIP_SGEIP | MIP_VSSIP | MIP_VSTIP | MIP_VSEIP); + + return riscv_cpu_pending_to_irq(env, IRQ_M_EXT, IPRIO_DEFAULT_M, + irqs, env->miprio); +} + +int riscv_cpu_sirq_pending(CPURISCVState *env) +{ + uint64_t irqs = riscv_cpu_all_pending(env) & env->mideleg & + ~(MIP_VSSIP | MIP_VSTIP | MIP_VSEIP); + + return riscv_cpu_pending_to_irq(env, IRQ_S_EXT, IPRIO_DEFAULT_S, + irqs, env->siprio); +} + +int riscv_cpu_vsirq_pending(CPURISCVState *env) +{ + uint64_t irqs = riscv_cpu_all_pending(env) & env->mideleg & + (MIP_VSSIP | MIP_VSTIP | MIP_VSEIP); + + return riscv_cpu_pending_to_irq(env, IRQ_S_EXT, IPRIO_DEFAULT_S, + irqs >> 1, env->hviprio); +} + static int riscv_cpu_local_irq_pending(CPURISCVState *env) { - target_ulong virt_enabled = riscv_cpu_virt_enabled(env); + int virq; + uint64_t irqs, pending, mie, hsie, vsie; - target_ulong mstatus_mie = get_field(env->mstatus, MSTATUS_MIE); - target_ulong mstatus_sie = get_field(env->mstatus, MSTATUS_SIE); - - target_ulong vsgemask = - (target_ulong)1 << get_field(env->hstatus, HSTATUS_VGEIN); - target_ulong vsgein = (env->hgeip & vsgemask) ? MIP_VSEIP : 0; - - target_ulong pending = (env->mip | vsgein) & env->mie; - - target_ulong mie = env->priv < PRV_M || - (env->priv == PRV_M && mstatus_mie); - target_ulong sie = env->priv < PRV_S || - (env->priv == PRV_S && mstatus_sie); - target_ulong hsie = virt_enabled || sie; - target_ulong vsie = virt_enabled && sie; - - target_ulong irqs = - (pending & ~env->mideleg & -mie) | - (pending & env->mideleg & ~env->hideleg & -hsie) | - (pending & env->mideleg & env->hideleg & -vsie); - - if (irqs) { - return ctz64(irqs); /* since non-zero */ + /* Determine interrupt enable state of all privilege modes */ + if (riscv_cpu_virt_enabled(env)) { + mie = 1; + hsie = 1; + vsie = (env->priv < PRV_S) || + (env->priv == PRV_S && get_field(env->mstatus, MSTATUS_SIE)); } else { - return RISCV_EXCP_NONE; /* indicates no pending interrupt */ + mie = (env->priv < PRV_M) || + (env->priv == PRV_M && get_field(env->mstatus, MSTATUS_MIE)); + hsie = (env->priv < PRV_S) || + (env->priv == PRV_S && get_field(env->mstatus, MSTATUS_SIE)); + vsie = 0; } + + /* Determine all pending interrupts */ + pending = riscv_cpu_all_pending(env); + + /* Check M-mode interrupts */ + irqs = pending & ~env->mideleg & -mie; + if (irqs) { + return riscv_cpu_pending_to_irq(env, IRQ_M_EXT, IPRIO_DEFAULT_M, + irqs, env->miprio); + } + + /* Check HS-mode interrupts */ + irqs = pending & env->mideleg & ~env->hideleg & -hsie; + if (irqs) { + return riscv_cpu_pending_to_irq(env, IRQ_S_EXT, IPRIO_DEFAULT_S, + irqs, env->siprio); + } + + /* Check VS-mode interrupts */ + irqs = pending & env->mideleg & env->hideleg & -vsie; + if (irqs) { + virq = riscv_cpu_pending_to_irq(env, IRQ_S_EXT, IPRIO_DEFAULT_S, + irqs >> 1, env->hviprio); + return (virq <= 0) ? virq : virq + 1; + } + + /* Indicate no pending interrupt */ + return RISCV_EXCP_NONE; } bool riscv_cpu_exec_interrupt(CPUState *cs, int interrupt_request) diff --git a/target/riscv/machine.c b/target/riscv/machine.c index 7d72c2d8a6..30ed77c25f 100644 --- a/target/riscv/machine.c +++ b/target/riscv/machine.c @@ -92,6 +92,7 @@ static const VMStateDescription vmstate_hyper = { VMSTATE_UINTTL(env.hgeie, RISCVCPU), VMSTATE_UINTTL(env.hgeip, RISCVCPU), VMSTATE_UINT64(env.htimedelta, RISCVCPU), + VMSTATE_UINT8_ARRAY(env.hviprio, RISCVCPU, 64), VMSTATE_UINT64(env.vsstatus, RISCVCPU), VMSTATE_UINTTL(env.vstvec, RISCVCPU), @@ -235,6 +236,8 @@ const VMStateDescription vmstate_riscv_cpu = { .fields = (VMStateField[]) { VMSTATE_UINTTL_ARRAY(env.gpr, RISCVCPU, 32), VMSTATE_UINT64_ARRAY(env.fpr, RISCVCPU, 32), + VMSTATE_UINT8_ARRAY(env.miprio, RISCVCPU, 64), + VMSTATE_UINT8_ARRAY(env.siprio, RISCVCPU, 64), VMSTATE_UINTTL(env.pc, RISCVCPU), VMSTATE_UINTTL(env.load_res, RISCVCPU), VMSTATE_UINTTL(env.load_val, RISCVCPU), From d028ac7512f1a781a5cba7659a1d25dc972afdd4 Mon Sep 17 00:00:00 2001 From: Anup Patel Date: Fri, 4 Feb 2022 23:16:46 +0530 Subject: [PATCH 446/460] target/riscv: Implement AIA CSRs for 64 local interrupts on RV32 The AIA specification adds new CSRs for RV32 so that RISC-V hart can support 64 local interrupts on both RV32 and RV64. Signed-off-by: Anup Patel Signed-off-by: Anup Patel Reviewed-by: Alistair Francis Reviewed-by: Frank Chang Message-id: 20220204174700.534953-11-anup@brainfault.org Signed-off-by: Alistair Francis --- target/riscv/cpu.h | 14 +- target/riscv/cpu_helper.c | 10 +- target/riscv/csr.c | 590 ++++++++++++++++++++++++++++++-------- target/riscv/machine.c | 10 +- 4 files changed, 489 insertions(+), 135 deletions(-) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 89e9cc558d..2dc2485bb4 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -172,12 +172,12 @@ struct CPURISCVState { */ uint64_t mstatus; - target_ulong mip; + uint64_t mip; - uint32_t miclaim; + uint64_t miclaim; - target_ulong mie; - target_ulong mideleg; + uint64_t mie; + uint64_t mideleg; target_ulong satp; /* since: priv-1.10.0 */ target_ulong stval; @@ -199,7 +199,7 @@ struct CPURISCVState { /* Hypervisor CSRs */ target_ulong hstatus; target_ulong hedeleg; - target_ulong hideleg; + uint64_t hideleg; target_ulong hcounteren; target_ulong htval; target_ulong htinst; @@ -456,8 +456,8 @@ void riscv_cpu_list(void); #ifndef CONFIG_USER_ONLY bool riscv_cpu_exec_interrupt(CPUState *cs, int interrupt_request); void riscv_cpu_swap_hypervisor_regs(CPURISCVState *env); -int riscv_cpu_claim_interrupts(RISCVCPU *cpu, uint32_t interrupts); -uint32_t riscv_cpu_update_mip(RISCVCPU *cpu, uint32_t mask, uint32_t value); +int riscv_cpu_claim_interrupts(RISCVCPU *cpu, uint64_t interrupts); +uint64_t riscv_cpu_update_mip(RISCVCPU *cpu, uint64_t mask, uint64_t value); #define BOOL_TO_MASK(x) (-!!(x)) /* helper for riscv_cpu_update_mip value */ void riscv_cpu_set_rdtime_fn(CPURISCVState *env, uint64_t (*fn)(uint32_t), uint32_t arg); diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 1a9534d6d7..430060dcd8 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -585,7 +585,7 @@ bool riscv_cpu_two_stage_lookup(int mmu_idx) return mmu_idx & TB_FLAGS_PRIV_HYP_ACCESS_MASK; } -int riscv_cpu_claim_interrupts(RISCVCPU *cpu, uint32_t interrupts) +int riscv_cpu_claim_interrupts(RISCVCPU *cpu, uint64_t interrupts) { CPURISCVState *env = &cpu->env; if (env->miclaim & interrupts) { @@ -596,11 +596,11 @@ int riscv_cpu_claim_interrupts(RISCVCPU *cpu, uint32_t interrupts) } } -uint32_t riscv_cpu_update_mip(RISCVCPU *cpu, uint32_t mask, uint32_t value) +uint64_t riscv_cpu_update_mip(RISCVCPU *cpu, uint64_t mask, uint64_t value) { CPURISCVState *env = &cpu->env; CPUState *cs = CPU(cpu); - uint32_t gein, vsgein = 0, old = env->mip; + uint64_t gein, vsgein = 0, old = env->mip; bool locked = false; if (riscv_cpu_virt_enabled(env)) { @@ -1306,7 +1306,7 @@ void riscv_cpu_do_interrupt(CPUState *cs) */ bool async = !!(cs->exception_index & RISCV_EXCP_INT_FLAG); target_ulong cause = cs->exception_index & RISCV_EXCP_INT_MASK; - target_ulong deleg = async ? env->mideleg : env->medeleg; + uint64_t deleg = async ? env->mideleg : env->medeleg; target_ulong tval = 0; target_ulong htval = 0; target_ulong mtval2 = 0; @@ -1373,7 +1373,7 @@ void riscv_cpu_do_interrupt(CPUState *cs) cause < TARGET_LONG_BITS && ((deleg >> cause) & 1)) { /* handle the trap in S-mode */ if (riscv_has_ext(env, RVH)) { - target_ulong hdeleg = async ? env->hideleg : env->hedeleg; + uint64_t hdeleg = async ? env->hideleg : env->hedeleg; if (riscv_cpu_virt_enabled(env) && ((hdeleg >> cause) & 1)) { /* Trap to VS mode */ diff --git a/target/riscv/csr.c b/target/riscv/csr.c index b23195b479..d8283160b1 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -158,6 +158,15 @@ static RISCVException any32(CPURISCVState *env, int csrno) } +static int aia_any32(CPURISCVState *env, int csrno) +{ + if (!riscv_feature(env, RISCV_FEATURE_AIA)) { + return RISCV_EXCP_ILLEGAL_INST; + } + + return any32(env, csrno); +} + static RISCVException smode(CPURISCVState *env, int csrno) { if (riscv_has_ext(env, RVS)) { @@ -167,6 +176,24 @@ static RISCVException smode(CPURISCVState *env, int csrno) return RISCV_EXCP_ILLEGAL_INST; } +static int smode32(CPURISCVState *env, int csrno) +{ + if (riscv_cpu_mxl(env) != MXL_RV32) { + return RISCV_EXCP_ILLEGAL_INST; + } + + return smode(env, csrno); +} + +static int aia_smode32(CPURISCVState *env, int csrno) +{ + if (!riscv_feature(env, RISCV_FEATURE_AIA)) { + return RISCV_EXCP_ILLEGAL_INST; + } + + return smode32(env, csrno); +} + static RISCVException hmode(CPURISCVState *env, int csrno) { if (riscv_has_ext(env, RVS) && @@ -207,6 +234,15 @@ static RISCVException pointer_masking(CPURISCVState *env, int csrno) return RISCV_EXCP_ILLEGAL_INST; } +static int aia_hmode32(CPURISCVState *env, int csrno) +{ + if (!riscv_feature(env, RISCV_FEATURE_AIA)) { + return RISCV_EXCP_ILLEGAL_INST; + } + + return hmode32(env, csrno); +} + static RISCVException pmp(CPURISCVState *env, int csrno) { if (riscv_feature(env, RISCV_FEATURE_PMP)) { @@ -458,15 +494,15 @@ static RISCVException read_timeh(CPURISCVState *env, int csrno, /* Machine constants */ -#define M_MODE_INTERRUPTS (MIP_MSIP | MIP_MTIP | MIP_MEIP) -#define S_MODE_INTERRUPTS (MIP_SSIP | MIP_STIP | MIP_SEIP) -#define VS_MODE_INTERRUPTS (MIP_VSSIP | MIP_VSTIP | MIP_VSEIP) -#define HS_MODE_INTERRUPTS (MIP_SGEIP | VS_MODE_INTERRUPTS) +#define M_MODE_INTERRUPTS ((uint64_t)(MIP_MSIP | MIP_MTIP | MIP_MEIP)) +#define S_MODE_INTERRUPTS ((uint64_t)(MIP_SSIP | MIP_STIP | MIP_SEIP)) +#define VS_MODE_INTERRUPTS ((uint64_t)(MIP_VSSIP | MIP_VSTIP | MIP_VSEIP)) +#define HS_MODE_INTERRUPTS ((uint64_t)(MIP_SGEIP | VS_MODE_INTERRUPTS)) -static const target_ulong delegable_ints = S_MODE_INTERRUPTS | +static const uint64_t delegable_ints = S_MODE_INTERRUPTS | VS_MODE_INTERRUPTS; -static const target_ulong vs_delegable_ints = VS_MODE_INTERRUPTS; -static const target_ulong all_ints = M_MODE_INTERRUPTS | S_MODE_INTERRUPTS | +static const uint64_t vs_delegable_ints = VS_MODE_INTERRUPTS; +static const uint64_t all_ints = M_MODE_INTERRUPTS | S_MODE_INTERRUPTS | HS_MODE_INTERRUPTS; #define DELEGABLE_EXCPS ((1ULL << (RISCV_EXCP_INST_ADDR_MIS)) | \ (1ULL << (RISCV_EXCP_INST_ACCESS_FAULT)) | \ @@ -737,40 +773,107 @@ static RISCVException write_medeleg(CPURISCVState *env, int csrno, return RISCV_EXCP_NONE; } -static RISCVException read_mideleg(CPURISCVState *env, int csrno, - target_ulong *val) +static RISCVException rmw_mideleg64(CPURISCVState *env, int csrno, + uint64_t *ret_val, + uint64_t new_val, uint64_t wr_mask) { - *val = env->mideleg; - return RISCV_EXCP_NONE; -} + uint64_t mask = wr_mask & delegable_ints; + + if (ret_val) { + *ret_val = env->mideleg; + } + + env->mideleg = (env->mideleg & ~mask) | (new_val & mask); -static RISCVException write_mideleg(CPURISCVState *env, int csrno, - target_ulong val) -{ - env->mideleg = (env->mideleg & ~delegable_ints) | (val & delegable_ints); if (riscv_has_ext(env, RVH)) { env->mideleg |= HS_MODE_INTERRUPTS; } + return RISCV_EXCP_NONE; } -static RISCVException read_mie(CPURISCVState *env, int csrno, - target_ulong *val) +static RISCVException rmw_mideleg(CPURISCVState *env, int csrno, + target_ulong *ret_val, + target_ulong new_val, target_ulong wr_mask) { - *val = env->mie; - return RISCV_EXCP_NONE; -} + uint64_t rval; + RISCVException ret; -static RISCVException write_mie(CPURISCVState *env, int csrno, - target_ulong val) -{ - env->mie = (env->mie & ~all_ints) | (val & all_ints); - if (!riscv_has_ext(env, RVH)) { - env->mie &= ~MIP_SGEIP; + ret = rmw_mideleg64(env, csrno, &rval, new_val, wr_mask); + if (ret_val) { + *ret_val = rval; } + + return ret; +} + +static RISCVException rmw_midelegh(CPURISCVState *env, int csrno, + target_ulong *ret_val, + target_ulong new_val, + target_ulong wr_mask) +{ + uint64_t rval; + RISCVException ret; + + ret = rmw_mideleg64(env, csrno, &rval, + ((uint64_t)new_val) << 32, ((uint64_t)wr_mask) << 32); + if (ret_val) { + *ret_val = rval >> 32; + } + + return ret; +} + +static RISCVException rmw_mie64(CPURISCVState *env, int csrno, + uint64_t *ret_val, + uint64_t new_val, uint64_t wr_mask) +{ + uint64_t mask = wr_mask & all_ints; + + if (ret_val) { + *ret_val = env->mie; + } + + env->mie = (env->mie & ~mask) | (new_val & mask); + + if (!riscv_has_ext(env, RVH)) { + env->mie &= ~((uint64_t)MIP_SGEIP); + } + return RISCV_EXCP_NONE; } +static RISCVException rmw_mie(CPURISCVState *env, int csrno, + target_ulong *ret_val, + target_ulong new_val, target_ulong wr_mask) +{ + uint64_t rval; + RISCVException ret; + + ret = rmw_mie64(env, csrno, &rval, new_val, wr_mask); + if (ret_val) { + *ret_val = rval; + } + + return ret; +} + +static RISCVException rmw_mieh(CPURISCVState *env, int csrno, + target_ulong *ret_val, + target_ulong new_val, target_ulong wr_mask) +{ + uint64_t rval; + RISCVException ret; + + ret = rmw_mie64(env, csrno, &rval, + ((uint64_t)new_val) << 32, ((uint64_t)wr_mask) << 32); + if (ret_val) { + *ret_val = rval >> 32; + } + + return ret; +} + static RISCVException read_mtvec(CPURISCVState *env, int csrno, target_ulong *val) { @@ -876,17 +979,17 @@ static RISCVException write_mtval(CPURISCVState *env, int csrno, return RISCV_EXCP_NONE; } -static RISCVException rmw_mip(CPURISCVState *env, int csrno, - target_ulong *ret_value, - target_ulong new_value, target_ulong write_mask) +static RISCVException rmw_mip64(CPURISCVState *env, int csrno, + uint64_t *ret_val, + uint64_t new_val, uint64_t wr_mask) { RISCVCPU *cpu = env_archcpu(env); /* Allow software control of delegable interrupts not claimed by hardware */ - target_ulong mask = write_mask & delegable_ints & ~env->miclaim; - uint32_t gin, old_mip; + uint64_t old_mip, mask = wr_mask & delegable_ints & ~env->miclaim; + uint32_t gin; if (mask) { - old_mip = riscv_cpu_update_mip(cpu, mask, (new_value & mask)); + old_mip = riscv_cpu_update_mip(cpu, mask, (new_val & mask)); } else { old_mip = env->mip; } @@ -896,13 +999,44 @@ static RISCVException rmw_mip(CPURISCVState *env, int csrno, old_mip |= (env->hgeip & ((target_ulong)1 << gin)) ? MIP_VSEIP : 0; } - if (ret_value) { - *ret_value = old_mip; + if (ret_val) { + *ret_val = old_mip; } return RISCV_EXCP_NONE; } +static RISCVException rmw_mip(CPURISCVState *env, int csrno, + target_ulong *ret_val, + target_ulong new_val, target_ulong wr_mask) +{ + uint64_t rval; + RISCVException ret; + + ret = rmw_mip64(env, csrno, &rval, new_val, wr_mask); + if (ret_val) { + *ret_val = rval; + } + + return ret; +} + +static RISCVException rmw_miph(CPURISCVState *env, int csrno, + target_ulong *ret_val, + target_ulong new_val, target_ulong wr_mask) +{ + uint64_t rval; + RISCVException ret; + + ret = rmw_mip64(env, csrno, &rval, + ((uint64_t)new_val) << 32, ((uint64_t)wr_mask) << 32); + if (ret_val) { + *ret_val = rval >> 32; + } + + return ret; +} + /* Supervisor Trap Setup */ static RISCVException read_sstatus_i128(CPURISCVState *env, int csrno, Int128 *val) @@ -943,45 +1077,112 @@ static RISCVException write_sstatus(CPURISCVState *env, int csrno, return write_mstatus(env, CSR_MSTATUS, newval); } -static RISCVException read_vsie(CPURISCVState *env, int csrno, - target_ulong *val) +static RISCVException rmw_vsie64(CPURISCVState *env, int csrno, + uint64_t *ret_val, + uint64_t new_val, uint64_t wr_mask) { - /* Shift the VS bits to their S bit location in vsie */ - *val = (env->mie & env->hideleg & VS_MODE_INTERRUPTS) >> 1; - return RISCV_EXCP_NONE; -} + RISCVException ret; + uint64_t rval, vsbits, mask = env->hideleg & VS_MODE_INTERRUPTS; -static RISCVException read_sie(CPURISCVState *env, int csrno, - target_ulong *val) -{ - if (riscv_cpu_virt_enabled(env)) { - read_vsie(env, CSR_VSIE, val); - } else { - *val = env->mie & env->mideleg; - } - return RISCV_EXCP_NONE; -} + /* Bring VS-level bits to correct position */ + vsbits = new_val & (VS_MODE_INTERRUPTS >> 1); + new_val &= ~(VS_MODE_INTERRUPTS >> 1); + new_val |= vsbits << 1; + vsbits = wr_mask & (VS_MODE_INTERRUPTS >> 1); + wr_mask &= ~(VS_MODE_INTERRUPTS >> 1); + wr_mask |= vsbits << 1; -static RISCVException write_vsie(CPURISCVState *env, int csrno, - target_ulong val) -{ - /* Shift the S bits to their VS bit location in mie */ - target_ulong newval = (env->mie & ~VS_MODE_INTERRUPTS) | - ((val << 1) & env->hideleg & VS_MODE_INTERRUPTS); - return write_mie(env, CSR_MIE, newval); -} - -static int write_sie(CPURISCVState *env, int csrno, target_ulong val) -{ - if (riscv_cpu_virt_enabled(env)) { - write_vsie(env, CSR_VSIE, val); - } else { - target_ulong newval = (env->mie & ~S_MODE_INTERRUPTS) | - (val & S_MODE_INTERRUPTS); - write_mie(env, CSR_MIE, newval); + ret = rmw_mie64(env, csrno, &rval, new_val, wr_mask & mask); + if (ret_val) { + rval &= mask; + vsbits = rval & VS_MODE_INTERRUPTS; + rval &= ~VS_MODE_INTERRUPTS; + *ret_val = rval | (vsbits >> 1); } - return RISCV_EXCP_NONE; + return ret; +} + +static RISCVException rmw_vsie(CPURISCVState *env, int csrno, + target_ulong *ret_val, + target_ulong new_val, target_ulong wr_mask) +{ + uint64_t rval; + RISCVException ret; + + ret = rmw_vsie64(env, csrno, &rval, new_val, wr_mask); + if (ret_val) { + *ret_val = rval; + } + + return ret; +} + +static RISCVException rmw_vsieh(CPURISCVState *env, int csrno, + target_ulong *ret_val, + target_ulong new_val, target_ulong wr_mask) +{ + uint64_t rval; + RISCVException ret; + + ret = rmw_vsie64(env, csrno, &rval, + ((uint64_t)new_val) << 32, ((uint64_t)wr_mask) << 32); + if (ret_val) { + *ret_val = rval >> 32; + } + + return ret; +} + +static RISCVException rmw_sie64(CPURISCVState *env, int csrno, + uint64_t *ret_val, + uint64_t new_val, uint64_t wr_mask) +{ + RISCVException ret; + uint64_t mask = env->mideleg & S_MODE_INTERRUPTS; + + if (riscv_cpu_virt_enabled(env)) { + ret = rmw_vsie64(env, CSR_VSIE, ret_val, new_val, wr_mask); + } else { + ret = rmw_mie64(env, csrno, ret_val, new_val, wr_mask & mask); + } + + if (ret_val) { + *ret_val &= mask; + } + + return ret; +} + +static RISCVException rmw_sie(CPURISCVState *env, int csrno, + target_ulong *ret_val, + target_ulong new_val, target_ulong wr_mask) +{ + uint64_t rval; + RISCVException ret; + + ret = rmw_sie64(env, csrno, &rval, new_val, wr_mask); + if (ret_val) { + *ret_val = rval; + } + + return ret; +} + +static RISCVException rmw_sieh(CPURISCVState *env, int csrno, + target_ulong *ret_val, + target_ulong new_val, target_ulong wr_mask) +{ + uint64_t rval; + RISCVException ret; + + ret = rmw_sie64(env, csrno, &rval, + ((uint64_t)new_val) << 32, ((uint64_t)wr_mask) << 32); + if (ret_val) { + *ret_val = rval >> 32; + } + + return ret; } static RISCVException read_stvec(CPURISCVState *env, int csrno, @@ -1089,38 +1290,111 @@ static RISCVException write_stval(CPURISCVState *env, int csrno, return RISCV_EXCP_NONE; } -static RISCVException rmw_vsip(CPURISCVState *env, int csrno, - target_ulong *ret_value, - target_ulong new_value, target_ulong write_mask) +static RISCVException rmw_vsip64(CPURISCVState *env, int csrno, + uint64_t *ret_val, + uint64_t new_val, uint64_t wr_mask) { - /* Shift the S bits to their VS bit location in mip */ - int ret = rmw_mip(env, csrno, ret_value, new_value << 1, - (write_mask << 1) & vsip_writable_mask & env->hideleg); + RISCVException ret; + uint64_t rval, vsbits, mask = env->hideleg & vsip_writable_mask; - if (ret_value) { - *ret_value &= VS_MODE_INTERRUPTS; - /* Shift the VS bits to their S bit location in vsip */ - *ret_value >>= 1; + /* Bring VS-level bits to correct position */ + vsbits = new_val & (VS_MODE_INTERRUPTS >> 1); + new_val &= ~(VS_MODE_INTERRUPTS >> 1); + new_val |= vsbits << 1; + vsbits = wr_mask & (VS_MODE_INTERRUPTS >> 1); + wr_mask &= ~(VS_MODE_INTERRUPTS >> 1); + wr_mask |= vsbits << 1; + + ret = rmw_mip64(env, csrno, &rval, new_val, wr_mask & mask); + if (ret_val) { + rval &= mask; + vsbits = rval & VS_MODE_INTERRUPTS; + rval &= ~VS_MODE_INTERRUPTS; + *ret_val = rval | (vsbits >> 1); } + + return ret; +} + +static RISCVException rmw_vsip(CPURISCVState *env, int csrno, + target_ulong *ret_val, + target_ulong new_val, target_ulong wr_mask) +{ + uint64_t rval; + RISCVException ret; + + ret = rmw_vsip64(env, csrno, &rval, new_val, wr_mask); + if (ret_val) { + *ret_val = rval; + } + + return ret; +} + +static RISCVException rmw_vsiph(CPURISCVState *env, int csrno, + target_ulong *ret_val, + target_ulong new_val, target_ulong wr_mask) +{ + uint64_t rval; + RISCVException ret; + + ret = rmw_vsip64(env, csrno, &rval, + ((uint64_t)new_val) << 32, ((uint64_t)wr_mask) << 32); + if (ret_val) { + *ret_val = rval >> 32; + } + + return ret; +} + +static RISCVException rmw_sip64(CPURISCVState *env, int csrno, + uint64_t *ret_val, + uint64_t new_val, uint64_t wr_mask) +{ + RISCVException ret; + uint64_t mask = env->mideleg & sip_writable_mask; + + if (riscv_cpu_virt_enabled(env)) { + ret = rmw_vsip64(env, CSR_VSIP, ret_val, new_val, wr_mask); + } else { + ret = rmw_mip64(env, csrno, ret_val, new_val, wr_mask & mask); + } + + if (ret_val) { + *ret_val &= env->mideleg & S_MODE_INTERRUPTS; + } + return ret; } static RISCVException rmw_sip(CPURISCVState *env, int csrno, - target_ulong *ret_value, - target_ulong new_value, target_ulong write_mask) + target_ulong *ret_val, + target_ulong new_val, target_ulong wr_mask) { - int ret; + uint64_t rval; + RISCVException ret; - if (riscv_cpu_virt_enabled(env)) { - ret = rmw_vsip(env, CSR_VSIP, ret_value, new_value, write_mask); - } else { - ret = rmw_mip(env, csrno, ret_value, new_value, - write_mask & env->mideleg & sip_writable_mask); + ret = rmw_sip64(env, csrno, &rval, new_val, wr_mask); + if (ret_val) { + *ret_val = rval; } - if (ret_value) { - *ret_value &= env->mideleg & S_MODE_INTERRUPTS; + return ret; +} + +static RISCVException rmw_siph(CPURISCVState *env, int csrno, + target_ulong *ret_val, + target_ulong new_val, target_ulong wr_mask) +{ + uint64_t rval; + RISCVException ret; + + ret = rmw_sip64(env, csrno, &rval, + ((uint64_t)new_val) << 32, ((uint64_t)wr_mask) << 32); + if (ret_val) { + *ret_val = rval >> 32; } + return ret; } @@ -1215,30 +1489,94 @@ static RISCVException write_hedeleg(CPURISCVState *env, int csrno, return RISCV_EXCP_NONE; } -static RISCVException read_hideleg(CPURISCVState *env, int csrno, - target_ulong *val) +static RISCVException rmw_hideleg64(CPURISCVState *env, int csrno, + uint64_t *ret_val, + uint64_t new_val, uint64_t wr_mask) { - *val = env->hideleg; + uint64_t mask = wr_mask & vs_delegable_ints; + + if (ret_val) { + *ret_val = env->hideleg & vs_delegable_ints; + } + + env->hideleg = (env->hideleg & ~mask) | (new_val & mask); return RISCV_EXCP_NONE; } -static RISCVException write_hideleg(CPURISCVState *env, int csrno, - target_ulong val) +static RISCVException rmw_hideleg(CPURISCVState *env, int csrno, + target_ulong *ret_val, + target_ulong new_val, target_ulong wr_mask) { - env->hideleg = val & vs_delegable_ints; - return RISCV_EXCP_NONE; + uint64_t rval; + RISCVException ret; + + ret = rmw_hideleg64(env, csrno, &rval, new_val, wr_mask); + if (ret_val) { + *ret_val = rval; + } + + return ret; +} + +static RISCVException rmw_hidelegh(CPURISCVState *env, int csrno, + target_ulong *ret_val, + target_ulong new_val, target_ulong wr_mask) +{ + uint64_t rval; + RISCVException ret; + + ret = rmw_hideleg64(env, csrno, &rval, + ((uint64_t)new_val) << 32, ((uint64_t)wr_mask) << 32); + if (ret_val) { + *ret_val = rval >> 32; + } + + return ret; +} + +static RISCVException rmw_hvip64(CPURISCVState *env, int csrno, + uint64_t *ret_val, + uint64_t new_val, uint64_t wr_mask) +{ + RISCVException ret; + + ret = rmw_mip64(env, csrno, ret_val, new_val, + wr_mask & hvip_writable_mask); + if (ret_val) { + *ret_val &= VS_MODE_INTERRUPTS; + } + + return ret; } static RISCVException rmw_hvip(CPURISCVState *env, int csrno, - target_ulong *ret_value, - target_ulong new_value, target_ulong write_mask) + target_ulong *ret_val, + target_ulong new_val, target_ulong wr_mask) { - int ret = rmw_mip(env, csrno, ret_value, new_value, - write_mask & hvip_writable_mask); + uint64_t rval; + RISCVException ret; - if (ret_value) { - *ret_value &= VS_MODE_INTERRUPTS; + ret = rmw_hvip64(env, csrno, &rval, new_val, wr_mask); + if (ret_val) { + *ret_val = rval; } + + return ret; +} + +static RISCVException rmw_hviph(CPURISCVState *env, int csrno, + target_ulong *ret_val, + target_ulong new_val, target_ulong wr_mask) +{ + uint64_t rval; + RISCVException ret; + + ret = rmw_hvip64(env, csrno, &rval, + ((uint64_t)new_val) << 32, ((uint64_t)wr_mask) << 32); + if (ret_val) { + *ret_val = rval >> 32; + } + return ret; } @@ -1255,18 +1593,19 @@ static RISCVException rmw_hip(CPURISCVState *env, int csrno, return ret; } -static RISCVException read_hie(CPURISCVState *env, int csrno, - target_ulong *val) +static RISCVException rmw_hie(CPURISCVState *env, int csrno, + target_ulong *ret_val, + target_ulong new_val, target_ulong wr_mask) { - *val = env->mie & HS_MODE_INTERRUPTS; - return RISCV_EXCP_NONE; -} + uint64_t rval; + RISCVException ret; -static RISCVException write_hie(CPURISCVState *env, int csrno, - target_ulong val) -{ - target_ulong newval = (env->mie & ~HS_MODE_INTERRUPTS) | (val & HS_MODE_INTERRUPTS); - return write_mie(env, CSR_MIE, newval); + ret = rmw_mie64(env, csrno, &rval, new_val, wr_mask & HS_MODE_INTERRUPTS); + if (ret_val) { + *ret_val = rval & HS_MODE_INTERRUPTS; + } + + return ret; } static RISCVException read_hcounteren(CPURISCVState *env, int csrno, @@ -2124,9 +2463,9 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { read_mstatus_i128 }, [CSR_MISA] = { "misa", any, read_misa, write_misa, NULL, read_misa_i128 }, - [CSR_MIDELEG] = { "mideleg", any, read_mideleg, write_mideleg }, + [CSR_MIDELEG] = { "mideleg", any, NULL, NULL, rmw_mideleg }, [CSR_MEDELEG] = { "medeleg", any, read_medeleg, write_medeleg }, - [CSR_MIE] = { "mie", any, read_mie, write_mie }, + [CSR_MIE] = { "mie", any, NULL, NULL, rmw_mie }, [CSR_MTVEC] = { "mtvec", any, read_mtvec, write_mtvec }, [CSR_MCOUNTEREN] = { "mcounteren", any, read_mcounteren, write_mcounteren }, @@ -2140,10 +2479,15 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { [CSR_MTVAL] = { "mtval", any, read_mtval, write_mtval }, [CSR_MIP] = { "mip", any, NULL, NULL, rmw_mip }, + /* Machine-Level High-Half CSRs (AIA) */ + [CSR_MIDELEGH] = { "midelegh", aia_any32, NULL, NULL, rmw_midelegh }, + [CSR_MIEH] = { "mieh", aia_any32, NULL, NULL, rmw_mieh }, + [CSR_MIPH] = { "miph", aia_any32, NULL, NULL, rmw_miph }, + /* Supervisor Trap Setup */ [CSR_SSTATUS] = { "sstatus", smode, read_sstatus, write_sstatus, NULL, read_sstatus_i128 }, - [CSR_SIE] = { "sie", smode, read_sie, write_sie }, + [CSR_SIE] = { "sie", smode, NULL, NULL, rmw_sie }, [CSR_STVEC] = { "stvec", smode, read_stvec, write_stvec }, [CSR_SCOUNTEREN] = { "scounteren", smode, read_scounteren, write_scounteren }, @@ -2158,12 +2502,16 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { /* Supervisor Protection and Translation */ [CSR_SATP] = { "satp", smode, read_satp, write_satp }, + /* Supervisor-Level High-Half CSRs (AIA) */ + [CSR_SIEH] = { "sieh", aia_smode32, NULL, NULL, rmw_sieh }, + [CSR_SIPH] = { "siph", aia_smode32, NULL, NULL, rmw_siph }, + [CSR_HSTATUS] = { "hstatus", hmode, read_hstatus, write_hstatus }, [CSR_HEDELEG] = { "hedeleg", hmode, read_hedeleg, write_hedeleg }, - [CSR_HIDELEG] = { "hideleg", hmode, read_hideleg, write_hideleg }, + [CSR_HIDELEG] = { "hideleg", hmode, NULL, NULL, rmw_hideleg }, [CSR_HVIP] = { "hvip", hmode, NULL, NULL, rmw_hvip }, [CSR_HIP] = { "hip", hmode, NULL, NULL, rmw_hip }, - [CSR_HIE] = { "hie", hmode, read_hie, write_hie }, + [CSR_HIE] = { "hie", hmode, NULL, NULL, rmw_hie }, [CSR_HCOUNTEREN] = { "hcounteren", hmode, read_hcounteren, write_hcounteren }, [CSR_HGEIE] = { "hgeie", hmode, read_hgeie, write_hgeie }, [CSR_HTVAL] = { "htval", hmode, read_htval, write_htval }, @@ -2175,7 +2523,7 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { [CSR_VSSTATUS] = { "vsstatus", hmode, read_vsstatus, write_vsstatus }, [CSR_VSIP] = { "vsip", hmode, NULL, NULL, rmw_vsip }, - [CSR_VSIE] = { "vsie", hmode, read_vsie, write_vsie }, + [CSR_VSIE] = { "vsie", hmode, NULL, NULL, rmw_vsie }, [CSR_VSTVEC] = { "vstvec", hmode, read_vstvec, write_vstvec }, [CSR_VSSCRATCH] = { "vsscratch", hmode, read_vsscratch, write_vsscratch }, [CSR_VSEPC] = { "vsepc", hmode, read_vsepc, write_vsepc }, @@ -2186,6 +2534,12 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { [CSR_MTVAL2] = { "mtval2", hmode, read_mtval2, write_mtval2 }, [CSR_MTINST] = { "mtinst", hmode, read_mtinst, write_mtinst }, + /* Hypervisor and VS-Level High-Half CSRs (H-extension with AIA) */ + [CSR_HIDELEGH] = { "hidelegh", aia_hmode32, NULL, NULL, rmw_hidelegh }, + [CSR_HVIPH] = { "hviph", aia_hmode32, NULL, NULL, rmw_hviph }, + [CSR_VSIEH] = { "vsieh", aia_hmode32, NULL, NULL, rmw_vsieh }, + [CSR_VSIPH] = { "vsiph", aia_hmode32, NULL, NULL, rmw_vsiph }, + /* Physical Memory Protection */ [CSR_MSECCFG] = { "mseccfg", epmp, read_mseccfg, write_mseccfg }, [CSR_PMPCFG0] = { "pmpcfg0", pmp, read_pmpcfg, write_pmpcfg }, diff --git a/target/riscv/machine.c b/target/riscv/machine.c index 30ed77c25f..65e63031ba 100644 --- a/target/riscv/machine.c +++ b/target/riscv/machine.c @@ -84,7 +84,7 @@ static const VMStateDescription vmstate_hyper = { .fields = (VMStateField[]) { VMSTATE_UINTTL(env.hstatus, RISCVCPU), VMSTATE_UINTTL(env.hedeleg, RISCVCPU), - VMSTATE_UINTTL(env.hideleg, RISCVCPU), + VMSTATE_UINT64(env.hideleg, RISCVCPU), VMSTATE_UINTTL(env.hcounteren, RISCVCPU), VMSTATE_UINTTL(env.htval, RISCVCPU), VMSTATE_UINTTL(env.htinst, RISCVCPU), @@ -256,10 +256,10 @@ const VMStateDescription vmstate_riscv_cpu = { VMSTATE_UINTTL(env.resetvec, RISCVCPU), VMSTATE_UINTTL(env.mhartid, RISCVCPU), VMSTATE_UINT64(env.mstatus, RISCVCPU), - VMSTATE_UINTTL(env.mip, RISCVCPU), - VMSTATE_UINT32(env.miclaim, RISCVCPU), - VMSTATE_UINTTL(env.mie, RISCVCPU), - VMSTATE_UINTTL(env.mideleg, RISCVCPU), + VMSTATE_UINT64(env.mip, RISCVCPU), + VMSTATE_UINT64(env.miclaim, RISCVCPU), + VMSTATE_UINT64(env.mie, RISCVCPU), + VMSTATE_UINT64(env.mideleg, RISCVCPU), VMSTATE_UINTTL(env.satp, RISCVCPU), VMSTATE_UINTTL(env.stval, RISCVCPU), VMSTATE_UINTTL(env.medeleg, RISCVCPU), From 2b6023987955a887aae3ad6882557960b2253a4f Mon Sep 17 00:00:00 2001 From: Anup Patel Date: Fri, 4 Feb 2022 23:16:47 +0530 Subject: [PATCH 447/460] target/riscv: Implement AIA hvictl and hviprioX CSRs The AIA hvictl and hviprioX CSRs allow hypervisor to control interrupts visible at VS-level. This patch implements AIA hvictl and hviprioX CSRs. Signed-off-by: Anup Patel Signed-off-by: Anup Patel Reviewed-by: Alistair Francis Reviewed-by: Frank Chang Message-id: 20220204174700.534953-12-anup@brainfault.org [ Changes by AF: - Fix possible unintilised variable error in rmw_sie() ] Signed-off-by: Alistair Francis --- target/riscv/cpu.h | 2 + target/riscv/csr.c | 128 ++++++++++++++++++++++++++++++++++++++++- target/riscv/machine.c | 2 + 3 files changed, 131 insertions(+), 1 deletion(-) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 2dc2485bb4..f0e69f2871 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -209,6 +209,7 @@ struct CPURISCVState { uint64_t htimedelta; /* Hypervisor controlled virtual interrupt priorities */ + target_ulong hvictl; uint8_t hviprio[64]; /* Upper 64-bits of 128-bit CSRs */ @@ -512,6 +513,7 @@ static inline RISCVMXL riscv_cpu_mxl(CPURISCVState *env) return env->misa_mxl; } #endif +#define riscv_cpu_mxl_bits(env) (1UL << (4 + riscv_cpu_mxl(env))) #if defined(TARGET_RISCV32) #define cpu_recompute_xl(env) ((void)(env), MXL_RV32) diff --git a/target/riscv/csr.c b/target/riscv/csr.c index d8283160b1..46448a2b7e 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -234,6 +234,15 @@ static RISCVException pointer_masking(CPURISCVState *env, int csrno) return RISCV_EXCP_ILLEGAL_INST; } +static int aia_hmode(CPURISCVState *env, int csrno) +{ + if (!riscv_feature(env, RISCV_FEATURE_AIA)) { + return RISCV_EXCP_ILLEGAL_INST; + } + + return hmode(env, csrno); +} + static int aia_hmode32(CPURISCVState *env, int csrno) { if (!riscv_feature(env, RISCV_FEATURE_AIA)) { @@ -1142,6 +1151,9 @@ static RISCVException rmw_sie64(CPURISCVState *env, int csrno, uint64_t mask = env->mideleg & S_MODE_INTERRUPTS; if (riscv_cpu_virt_enabled(env)) { + if (env->hvictl & HVICTL_VTI) { + return RISCV_EXCP_VIRT_INSTRUCTION_FAULT; + } ret = rmw_vsie64(env, CSR_VSIE, ret_val, new_val, wr_mask); } else { ret = rmw_mie64(env, csrno, ret_val, new_val, wr_mask & mask); @@ -1162,7 +1174,7 @@ static RISCVException rmw_sie(CPURISCVState *env, int csrno, RISCVException ret; ret = rmw_sie64(env, csrno, &rval, new_val, wr_mask); - if (ret_val) { + if (ret == RISCV_EXCP_NONE && ret_val) { *ret_val = rval; } @@ -1355,6 +1367,9 @@ static RISCVException rmw_sip64(CPURISCVState *env, int csrno, uint64_t mask = env->mideleg & sip_writable_mask; if (riscv_cpu_virt_enabled(env)) { + if (env->hvictl & HVICTL_VTI) { + return RISCV_EXCP_VIRT_INSTRUCTION_FAULT; + } ret = rmw_vsip64(env, CSR_VSIP, ret_val, new_val, wr_mask); } else { ret = rmw_mip64(env, csrno, ret_val, new_val, wr_mask & mask); @@ -1741,6 +1756,110 @@ static RISCVException write_htimedeltah(CPURISCVState *env, int csrno, return RISCV_EXCP_NONE; } +static int read_hvictl(CPURISCVState *env, int csrno, target_ulong *val) +{ + *val = env->hvictl; + return RISCV_EXCP_NONE; +} + +static int write_hvictl(CPURISCVState *env, int csrno, target_ulong val) +{ + env->hvictl = val & HVICTL_VALID_MASK; + return RISCV_EXCP_NONE; +} + +static int read_hvipriox(CPURISCVState *env, int first_index, + uint8_t *iprio, target_ulong *val) +{ + int i, irq, rdzero, num_irqs = 4 * (riscv_cpu_mxl_bits(env) / 32); + + /* First index has to be a multiple of number of irqs per register */ + if (first_index % num_irqs) { + return (riscv_cpu_virt_enabled(env)) ? + RISCV_EXCP_VIRT_INSTRUCTION_FAULT : RISCV_EXCP_ILLEGAL_INST; + } + + /* Fill-up return value */ + *val = 0; + for (i = 0; i < num_irqs; i++) { + if (riscv_cpu_hviprio_index2irq(first_index + i, &irq, &rdzero)) { + continue; + } + if (rdzero) { + continue; + } + *val |= ((target_ulong)iprio[irq]) << (i * 8); + } + + return RISCV_EXCP_NONE; +} + +static int write_hvipriox(CPURISCVState *env, int first_index, + uint8_t *iprio, target_ulong val) +{ + int i, irq, rdzero, num_irqs = 4 * (riscv_cpu_mxl_bits(env) / 32); + + /* First index has to be a multiple of number of irqs per register */ + if (first_index % num_irqs) { + return (riscv_cpu_virt_enabled(env)) ? + RISCV_EXCP_VIRT_INSTRUCTION_FAULT : RISCV_EXCP_ILLEGAL_INST; + } + + /* Fill-up priority arrary */ + for (i = 0; i < num_irqs; i++) { + if (riscv_cpu_hviprio_index2irq(first_index + i, &irq, &rdzero)) { + continue; + } + if (rdzero) { + iprio[irq] = 0; + } else { + iprio[irq] = (val >> (i * 8)) & 0xff; + } + } + + return RISCV_EXCP_NONE; +} + +static int read_hviprio1(CPURISCVState *env, int csrno, target_ulong *val) +{ + return read_hvipriox(env, 0, env->hviprio, val); +} + +static int write_hviprio1(CPURISCVState *env, int csrno, target_ulong val) +{ + return write_hvipriox(env, 0, env->hviprio, val); +} + +static int read_hviprio1h(CPURISCVState *env, int csrno, target_ulong *val) +{ + return read_hvipriox(env, 4, env->hviprio, val); +} + +static int write_hviprio1h(CPURISCVState *env, int csrno, target_ulong val) +{ + return write_hvipriox(env, 4, env->hviprio, val); +} + +static int read_hviprio2(CPURISCVState *env, int csrno, target_ulong *val) +{ + return read_hvipriox(env, 8, env->hviprio, val); +} + +static int write_hviprio2(CPURISCVState *env, int csrno, target_ulong val) +{ + return write_hvipriox(env, 8, env->hviprio, val); +} + +static int read_hviprio2h(CPURISCVState *env, int csrno, target_ulong *val) +{ + return read_hvipriox(env, 12, env->hviprio, val); +} + +static int write_hviprio2h(CPURISCVState *env, int csrno, target_ulong val) +{ + return write_hvipriox(env, 12, env->hviprio, val); +} + /* Virtual CSR Registers */ static RISCVException read_vsstatus(CPURISCVState *env, int csrno, target_ulong *val) @@ -2534,9 +2653,16 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { [CSR_MTVAL2] = { "mtval2", hmode, read_mtval2, write_mtval2 }, [CSR_MTINST] = { "mtinst", hmode, read_mtinst, write_mtinst }, + /* Virtual Interrupts and Interrupt Priorities (H-extension with AIA) */ + [CSR_HVICTL] = { "hvictl", aia_hmode, read_hvictl, write_hvictl }, + [CSR_HVIPRIO1] = { "hviprio1", aia_hmode, read_hviprio1, write_hviprio1 }, + [CSR_HVIPRIO2] = { "hviprio2", aia_hmode, read_hviprio2, write_hviprio2 }, + /* Hypervisor and VS-Level High-Half CSRs (H-extension with AIA) */ [CSR_HIDELEGH] = { "hidelegh", aia_hmode32, NULL, NULL, rmw_hidelegh }, [CSR_HVIPH] = { "hviph", aia_hmode32, NULL, NULL, rmw_hviph }, + [CSR_HVIPRIO1H] = { "hviprio1h", aia_hmode32, read_hviprio1h, write_hviprio1h }, + [CSR_HVIPRIO2H] = { "hviprio2h", aia_hmode32, read_hviprio2h, write_hviprio2h }, [CSR_VSIEH] = { "vsieh", aia_hmode32, NULL, NULL, rmw_vsieh }, [CSR_VSIPH] = { "vsiph", aia_hmode32, NULL, NULL, rmw_vsiph }, diff --git a/target/riscv/machine.c b/target/riscv/machine.c index 65e63031ba..dbd7bd0c83 100644 --- a/target/riscv/machine.c +++ b/target/riscv/machine.c @@ -92,6 +92,8 @@ static const VMStateDescription vmstate_hyper = { VMSTATE_UINTTL(env.hgeie, RISCVCPU), VMSTATE_UINTTL(env.hgeip, RISCVCPU), VMSTATE_UINT64(env.htimedelta, RISCVCPU), + + VMSTATE_UINTTL(env.hvictl, RISCVCPU), VMSTATE_UINT8_ARRAY(env.hviprio, RISCVCPU, 64), VMSTATE_UINT64(env.vsstatus, RISCVCPU), From d0237b4df07e7b532b9b917639d6eb6b2c825c67 Mon Sep 17 00:00:00 2001 From: Anup Patel Date: Fri, 4 Feb 2022 23:16:48 +0530 Subject: [PATCH 448/460] target/riscv: Implement AIA interrupt filtering CSRs The AIA specificaiton adds interrupt filtering support for M-mode and HS-mode. Using AIA interrupt filtering M-mode and H-mode can take local interrupt 13 or above and selectively inject same local interrupt to lower privilege modes. At the moment, we don't have any local interrupts above 12 so we add dummy implementation (i.e. read zero and ignore write) of AIA interrupt filtering CSRs. Signed-off-by: Anup Patel Signed-off-by: Anup Patel Reviewed-by: Alistair Francis Reviewed-by: Frank Chang Message-id: 20220204174700.534953-13-anup@brainfault.org Signed-off-by: Alistair Francis --- target/riscv/csr.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 46448a2b7e..89700038fb 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -158,6 +158,15 @@ static RISCVException any32(CPURISCVState *env, int csrno) } +static int aia_any(CPURISCVState *env, int csrno) +{ + if (!riscv_feature(env, RISCV_FEATURE_AIA)) { + return RISCV_EXCP_ILLEGAL_INST; + } + + return any(env, csrno); +} + static int aia_any32(CPURISCVState *env, int csrno) { if (!riscv_feature(env, RISCV_FEATURE_AIA)) { @@ -568,6 +577,12 @@ static RISCVException read_zero(CPURISCVState *env, int csrno, return RISCV_EXCP_NONE; } +static RISCVException write_ignore(CPURISCVState *env, int csrno, + target_ulong val) +{ + return RISCV_EXCP_NONE; +} + static RISCVException read_mhartid(CPURISCVState *env, int csrno, target_ulong *val) { @@ -2598,9 +2613,15 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { [CSR_MTVAL] = { "mtval", any, read_mtval, write_mtval }, [CSR_MIP] = { "mip", any, NULL, NULL, rmw_mip }, + /* Virtual Interrupts for Supervisor Level (AIA) */ + [CSR_MVIEN] = { "mvien", aia_any, read_zero, write_ignore }, + [CSR_MVIP] = { "mvip", aia_any, read_zero, write_ignore }, + /* Machine-Level High-Half CSRs (AIA) */ [CSR_MIDELEGH] = { "midelegh", aia_any32, NULL, NULL, rmw_midelegh }, [CSR_MIEH] = { "mieh", aia_any32, NULL, NULL, rmw_mieh }, + [CSR_MVIENH] = { "mvienh", aia_any32, read_zero, write_ignore }, + [CSR_MVIPH] = { "mviph", aia_any32, read_zero, write_ignore }, [CSR_MIPH] = { "miph", aia_any32, NULL, NULL, rmw_miph }, /* Supervisor Trap Setup */ @@ -2654,12 +2675,14 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { [CSR_MTINST] = { "mtinst", hmode, read_mtinst, write_mtinst }, /* Virtual Interrupts and Interrupt Priorities (H-extension with AIA) */ + [CSR_HVIEN] = { "hvien", aia_hmode, read_zero, write_ignore }, [CSR_HVICTL] = { "hvictl", aia_hmode, read_hvictl, write_hvictl }, [CSR_HVIPRIO1] = { "hviprio1", aia_hmode, read_hviprio1, write_hviprio1 }, [CSR_HVIPRIO2] = { "hviprio2", aia_hmode, read_hviprio2, write_hviprio2 }, /* Hypervisor and VS-Level High-Half CSRs (H-extension with AIA) */ [CSR_HIDELEGH] = { "hidelegh", aia_hmode32, NULL, NULL, rmw_hidelegh }, + [CSR_HVIENH] = { "hvienh", aia_hmode32, read_zero, write_ignore }, [CSR_HVIPH] = { "hviph", aia_hmode32, NULL, NULL, rmw_hviph }, [CSR_HVIPRIO1H] = { "hviprio1h", aia_hmode32, read_hviprio1h, write_hviprio1h }, [CSR_HVIPRIO2H] = { "hviprio2h", aia_hmode32, read_hviprio2h, write_hviprio2h }, From c7de92b4e829b0df4087371b23e41bbe8aec766d Mon Sep 17 00:00:00 2001 From: Anup Patel Date: Fri, 4 Feb 2022 23:16:49 +0530 Subject: [PATCH 449/460] target/riscv: Implement AIA mtopi, stopi, and vstopi CSRs The AIA specification introduces new [m|s|vs]topi CSRs for reporting pending local IRQ number and associated IRQ priority. Signed-off-by: Anup Patel Signed-off-by: Anup Patel Reviewed-by: Frank Chang Message-id: 20220204174700.534953-14-anup@brainfault.org [ Changed by AF: - Fixup indentation ] Signed-off-by: Alistair Francis --- target/riscv/csr.c | 156 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 156 insertions(+) diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 89700038fb..39402a6a49 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -194,6 +194,15 @@ static int smode32(CPURISCVState *env, int csrno) return smode(env, csrno); } +static int aia_smode(CPURISCVState *env, int csrno) +{ + if (!riscv_feature(env, RISCV_FEATURE_AIA)) { + return RISCV_EXCP_ILLEGAL_INST; + } + + return smode(env, csrno); +} + static int aia_smode32(CPURISCVState *env, int csrno) { if (!riscv_feature(env, RISCV_FEATURE_AIA)) { @@ -517,6 +526,8 @@ static RISCVException read_timeh(CPURISCVState *env, int csrno, #define VS_MODE_INTERRUPTS ((uint64_t)(MIP_VSSIP | MIP_VSTIP | MIP_VSEIP)) #define HS_MODE_INTERRUPTS ((uint64_t)(MIP_SGEIP | VS_MODE_INTERRUPTS)) +#define VSTOPI_NUM_SRCS 5 + static const uint64_t delegable_ints = S_MODE_INTERRUPTS | VS_MODE_INTERRUPTS; static const uint64_t vs_delegable_ints = VS_MODE_INTERRUPTS; @@ -898,6 +909,28 @@ static RISCVException rmw_mieh(CPURISCVState *env, int csrno, return ret; } +static int read_mtopi(CPURISCVState *env, int csrno, target_ulong *val) +{ + int irq; + uint8_t iprio; + + irq = riscv_cpu_mirq_pending(env); + if (irq <= 0 || irq > 63) { + *val = 0; + } else { + iprio = env->miprio[irq]; + if (!iprio) { + if (riscv_cpu_default_priority(irq) > IPRIO_DEFAULT_M) { + iprio = IPRIO_MMAXIPRIO; + } + } + *val = (irq & TOPI_IID_MASK) << TOPI_IID_SHIFT; + *val |= iprio; + } + + return RISCV_EXCP_NONE; +} + static RISCVException read_mtvec(CPURISCVState *env, int csrno, target_ulong *val) { @@ -1478,6 +1511,120 @@ static RISCVException write_satp(CPURISCVState *env, int csrno, return RISCV_EXCP_NONE; } +static int read_vstopi(CPURISCVState *env, int csrno, target_ulong *val) +{ + int irq, ret; + target_ulong topei; + uint64_t vseip, vsgein; + uint32_t iid, iprio, hviid, hviprio, gein; + uint32_t s, scount = 0, siid[VSTOPI_NUM_SRCS], siprio[VSTOPI_NUM_SRCS]; + + gein = get_field(env->hstatus, HSTATUS_VGEIN); + hviid = get_field(env->hvictl, HVICTL_IID); + hviprio = get_field(env->hvictl, HVICTL_IPRIO); + + if (gein) { + vsgein = (env->hgeip & (1ULL << gein)) ? MIP_VSEIP : 0; + vseip = env->mie & (env->mip | vsgein) & MIP_VSEIP; + if (gein <= env->geilen && vseip) { + siid[scount] = IRQ_S_EXT; + siprio[scount] = IPRIO_MMAXIPRIO + 1; + if (env->aia_ireg_rmw_fn[PRV_S]) { + /* + * Call machine specific IMSIC register emulation for + * reading TOPEI. + */ + ret = env->aia_ireg_rmw_fn[PRV_S]( + env->aia_ireg_rmw_fn_arg[PRV_S], + AIA_MAKE_IREG(ISELECT_IMSIC_TOPEI, PRV_S, true, gein, + riscv_cpu_mxl_bits(env)), + &topei, 0, 0); + if (!ret && topei) { + siprio[scount] = topei & IMSIC_TOPEI_IPRIO_MASK; + } + } + scount++; + } + } else { + if (hviid == IRQ_S_EXT && hviprio) { + siid[scount] = IRQ_S_EXT; + siprio[scount] = hviprio; + scount++; + } + } + + if (env->hvictl & HVICTL_VTI) { + if (hviid != IRQ_S_EXT) { + siid[scount] = hviid; + siprio[scount] = hviprio; + scount++; + } + } else { + irq = riscv_cpu_vsirq_pending(env); + if (irq != IRQ_S_EXT && 0 < irq && irq <= 63) { + siid[scount] = irq; + siprio[scount] = env->hviprio[irq]; + scount++; + } + } + + iid = 0; + iprio = UINT_MAX; + for (s = 0; s < scount; s++) { + if (siprio[s] < iprio) { + iid = siid[s]; + iprio = siprio[s]; + } + } + + if (iid) { + if (env->hvictl & HVICTL_IPRIOM) { + if (iprio > IPRIO_MMAXIPRIO) { + iprio = IPRIO_MMAXIPRIO; + } + if (!iprio) { + if (riscv_cpu_default_priority(iid) > IPRIO_DEFAULT_S) { + iprio = IPRIO_MMAXIPRIO; + } + } + } else { + iprio = 1; + } + } else { + iprio = 0; + } + + *val = (iid & TOPI_IID_MASK) << TOPI_IID_SHIFT; + *val |= iprio; + return RISCV_EXCP_NONE; +} + +static int read_stopi(CPURISCVState *env, int csrno, target_ulong *val) +{ + int irq; + uint8_t iprio; + + if (riscv_cpu_virt_enabled(env)) { + return read_vstopi(env, CSR_VSTOPI, val); + } + + irq = riscv_cpu_sirq_pending(env); + if (irq <= 0 || irq > 63) { + *val = 0; + } else { + iprio = env->siprio[irq]; + if (!iprio) { + if (riscv_cpu_default_priority(irq) > IPRIO_DEFAULT_S) { + iprio = IPRIO_MMAXIPRIO; + } + } + *val = (irq & TOPI_IID_MASK) << TOPI_IID_SHIFT; + *val |= iprio; + } + + return RISCV_EXCP_NONE; +} + /* Hypervisor Extensions */ static RISCVException read_hstatus(CPURISCVState *env, int csrno, target_ulong *val) @@ -2613,6 +2760,9 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { [CSR_MTVAL] = { "mtval", any, read_mtval, write_mtval }, [CSR_MIP] = { "mip", any, NULL, NULL, rmw_mip }, + /* Machine-Level Interrupts (AIA) */ + [CSR_MTOPI] = { "mtopi", aia_any, read_mtopi }, + /* Virtual Interrupts for Supervisor Level (AIA) */ [CSR_MVIEN] = { "mvien", aia_any, read_zero, write_ignore }, [CSR_MVIP] = { "mvip", aia_any, read_zero, write_ignore }, @@ -2642,6 +2792,9 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { /* Supervisor Protection and Translation */ [CSR_SATP] = { "satp", smode, read_satp, write_satp }, + /* Supervisor-Level Interrupts (AIA) */ + [CSR_STOPI] = { "stopi", aia_smode, read_stopi }, + /* Supervisor-Level High-Half CSRs (AIA) */ [CSR_SIEH] = { "sieh", aia_smode32, NULL, NULL, rmw_sieh }, [CSR_SIPH] = { "siph", aia_smode32, NULL, NULL, rmw_siph }, @@ -2680,6 +2833,9 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { [CSR_HVIPRIO1] = { "hviprio1", aia_hmode, read_hviprio1, write_hviprio1 }, [CSR_HVIPRIO2] = { "hviprio2", aia_hmode, read_hviprio2, write_hviprio2 }, + /* VS-Level Interrupts (H-extension with AIA) */ + [CSR_VSTOPI] = { "vstopi", aia_hmode, read_vstopi }, + /* Hypervisor and VS-Level High-Half CSRs (H-extension with AIA) */ [CSR_HIDELEGH] = { "hidelegh", aia_hmode32, NULL, NULL, rmw_hidelegh }, [CSR_HVIENH] = { "hvienh", aia_hmode32, read_zero, write_ignore }, From d1ceff405ae476127ec805ae99afa71d095a1bd2 Mon Sep 17 00:00:00 2001 From: Anup Patel Date: Fri, 4 Feb 2022 23:16:50 +0530 Subject: [PATCH 450/460] target/riscv: Implement AIA xiselect and xireg CSRs The AIA specification defines [m|s|vs]iselect and [m|s|vs]ireg CSRs which allow indirect access to interrupt priority arrays and per-HART IMSIC registers. This patch implements AIA xiselect and xireg CSRs. Signed-off-by: Anup Patel Signed-off-by: Anup Patel Reviewed-by: Frank Chang Message-id: 20220204174700.534953-15-anup@brainfault.org Signed-off-by: Alistair Francis --- target/riscv/cpu.h | 7 ++ target/riscv/csr.c | 177 +++++++++++++++++++++++++++++++++++++++++ target/riscv/machine.c | 3 + 3 files changed, 187 insertions(+) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index f0e69f2871..c70de10c85 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -196,6 +196,10 @@ struct CPURISCVState { uint8_t miprio[64]; uint8_t siprio[64]; + /* AIA CSRs */ + target_ulong miselect; + target_ulong siselect; + /* Hypervisor CSRs */ target_ulong hstatus; target_ulong hedeleg; @@ -229,6 +233,9 @@ struct CPURISCVState { target_ulong vstval; target_ulong vsatp; + /* AIA VS-mode CSRs */ + target_ulong vsiselect; + target_ulong mtval2; target_ulong mtinst; diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 39402a6a49..a186b31fcf 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -931,6 +931,169 @@ static int read_mtopi(CPURISCVState *env, int csrno, target_ulong *val) return RISCV_EXCP_NONE; } +static int aia_xlate_vs_csrno(CPURISCVState *env, int csrno) +{ + if (!riscv_cpu_virt_enabled(env)) { + return csrno; + } + + switch (csrno) { + case CSR_SISELECT: + return CSR_VSISELECT; + case CSR_SIREG: + return CSR_VSIREG; + default: + return csrno; + }; +} + +static int rmw_xiselect(CPURISCVState *env, int csrno, target_ulong *val, + target_ulong new_val, target_ulong wr_mask) +{ + target_ulong *iselect; + + /* Translate CSR number for VS-mode */ + csrno = aia_xlate_vs_csrno(env, csrno); + + /* Find the iselect CSR based on CSR number */ + switch (csrno) { + case CSR_MISELECT: + iselect = &env->miselect; + break; + case CSR_SISELECT: + iselect = &env->siselect; + break; + case CSR_VSISELECT: + iselect = &env->vsiselect; + break; + default: + return RISCV_EXCP_ILLEGAL_INST; + }; + + if (val) { + *val = *iselect; + } + + wr_mask &= ISELECT_MASK; + if (wr_mask) { + *iselect = (*iselect & ~wr_mask) | (new_val & wr_mask); + } + + return RISCV_EXCP_NONE; +} + +static int rmw_iprio(target_ulong xlen, + target_ulong iselect, uint8_t *iprio, + target_ulong *val, target_ulong new_val, + target_ulong wr_mask, int ext_irq_no) +{ + int i, firq, nirqs; + target_ulong old_val; + + if (iselect < ISELECT_IPRIO0 || ISELECT_IPRIO15 < iselect) { + return -EINVAL; + } + if (xlen != 32 && iselect & 0x1) { + return -EINVAL; + } + + nirqs = 4 * (xlen / 32); + firq = ((iselect - ISELECT_IPRIO0) / (xlen / 32)) * (nirqs); + + old_val = 0; + for (i = 0; i < nirqs; i++) { + old_val |= ((target_ulong)iprio[firq + i]) << (IPRIO_IRQ_BITS * i); + } + + if (val) { + *val = old_val; + } + + if (wr_mask) { + new_val = (old_val & ~wr_mask) | (new_val & wr_mask); + for (i = 0; i < nirqs; i++) { + /* + * M-level and S-level external IRQ priority always read-only + * zero. This means default priority order is always preferred + * for M-level and S-level external IRQs. + */ + if ((firq + i) == ext_irq_no) { + continue; + } + iprio[firq + i] = (new_val >> (IPRIO_IRQ_BITS * i)) & 0xff; + } + } + + return 0; +} + +static int rmw_xireg(CPURISCVState *env, int csrno, target_ulong *val, + target_ulong new_val, target_ulong wr_mask) +{ + bool virt; + uint8_t *iprio; + int ret = -EINVAL; + target_ulong priv, isel, vgein; + + /* Translate CSR number for VS-mode */ + csrno = aia_xlate_vs_csrno(env, csrno); + + /* Decode register details from CSR number */ + virt = false; + switch (csrno) { + case CSR_MIREG: + iprio = env->miprio; + isel = env->miselect; + priv = PRV_M; + break; + case CSR_SIREG: + iprio = env->siprio; + isel = env->siselect; + priv = PRV_S; + break; + case CSR_VSIREG: + iprio = env->hviprio; + isel = env->vsiselect; + priv = PRV_S; + virt = true; + break; + default: + goto done; + }; + + /* Find the selected guest interrupt file */ + vgein = (virt) ? get_field(env->hstatus, HSTATUS_VGEIN) : 0; + + if (ISELECT_IPRIO0 <= isel && isel <= ISELECT_IPRIO15) { + /* Local interrupt priority registers not available for VS-mode */ + if (!virt) { + ret = rmw_iprio(riscv_cpu_mxl_bits(env), + isel, iprio, val, new_val, wr_mask, + (priv == PRV_M) ? IRQ_M_EXT : IRQ_S_EXT); + } + } else if (ISELECT_IMSIC_FIRST <= isel && isel <= ISELECT_IMSIC_LAST) { + /* IMSIC registers only available when machine implements it. */ + if (env->aia_ireg_rmw_fn[priv]) { + /* Selected guest interrupt file should not be zero */ + if (virt && (!vgein || env->geilen < vgein)) { + goto done; + } + /* Call machine specific IMSIC register emulation */ + ret = env->aia_ireg_rmw_fn[priv](env->aia_ireg_rmw_fn_arg[priv], + AIA_MAKE_IREG(isel, priv, virt, vgein, + riscv_cpu_mxl_bits(env)), + val, new_val, wr_mask); + } + } + +done: + if (ret) { + return (riscv_cpu_virt_enabled(env) && virt) ? + RISCV_EXCP_VIRT_INSTRUCTION_FAULT : RISCV_EXCP_ILLEGAL_INST; + } + return RISCV_EXCP_NONE; +} + static RISCVException read_mtvec(CPURISCVState *env, int csrno, target_ulong *val) { @@ -2760,6 +2923,10 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { [CSR_MTVAL] = { "mtval", any, read_mtval, write_mtval }, [CSR_MIP] = { "mip", any, NULL, NULL, rmw_mip }, + /* Machine-Level Window to Indirectly Accessed Registers (AIA) */ + [CSR_MISELECT] = { "miselect", aia_any, NULL, NULL, rmw_xiselect }, + [CSR_MIREG] = { "mireg", aia_any, NULL, NULL, rmw_xireg }, + /* Machine-Level Interrupts (AIA) */ [CSR_MTOPI] = { "mtopi", aia_any, read_mtopi }, @@ -2792,6 +2959,10 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { /* Supervisor Protection and Translation */ [CSR_SATP] = { "satp", smode, read_satp, write_satp }, + /* Supervisor-Level Window to Indirectly Accessed Registers (AIA) */ + [CSR_SISELECT] = { "siselect", aia_smode, NULL, NULL, rmw_xiselect }, + [CSR_SIREG] = { "sireg", aia_smode, NULL, NULL, rmw_xireg }, + /* Supervisor-Level Interrupts (AIA) */ [CSR_STOPI] = { "stopi", aia_smode, read_stopi }, @@ -2833,6 +3004,12 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { [CSR_HVIPRIO1] = { "hviprio1", aia_hmode, read_hviprio1, write_hviprio1 }, [CSR_HVIPRIO2] = { "hviprio2", aia_hmode, read_hviprio2, write_hviprio2 }, + /* + * VS-Level Window to Indirectly Accessed Registers (H-extension with AIA) + */ + [CSR_VSISELECT] = { "vsiselect", aia_hmode, NULL, NULL, rmw_xiselect }, + [CSR_VSIREG] = { "vsireg", aia_hmode, NULL, NULL, rmw_xireg }, + /* VS-Level Interrupts (H-extension with AIA) */ [CSR_VSTOPI] = { "vstopi", aia_hmode, read_vstopi }, diff --git a/target/riscv/machine.c b/target/riscv/machine.c index dbd7bd0c83..5178b3fec9 100644 --- a/target/riscv/machine.c +++ b/target/riscv/machine.c @@ -103,6 +103,7 @@ static const VMStateDescription vmstate_hyper = { VMSTATE_UINTTL(env.vscause, RISCVCPU), VMSTATE_UINTTL(env.vstval, RISCVCPU), VMSTATE_UINTTL(env.vsatp, RISCVCPU), + VMSTATE_UINTTL(env.vsiselect, RISCVCPU), VMSTATE_UINTTL(env.mtval2, RISCVCPU), VMSTATE_UINTTL(env.mtinst, RISCVCPU), @@ -272,6 +273,8 @@ const VMStateDescription vmstate_riscv_cpu = { VMSTATE_UINTTL(env.mepc, RISCVCPU), VMSTATE_UINTTL(env.mcause, RISCVCPU), VMSTATE_UINTTL(env.mtval, RISCVCPU), + VMSTATE_UINTTL(env.miselect, RISCVCPU), + VMSTATE_UINTTL(env.siselect, RISCVCPU), VMSTATE_UINTTL(env.scounteren, RISCVCPU), VMSTATE_UINTTL(env.mcounteren, RISCVCPU), VMSTATE_UINTTL(env.sscratch, RISCVCPU), From ac4b0302b0ca986a759538f453b44037c7b66dd9 Mon Sep 17 00:00:00 2001 From: Anup Patel Date: Fri, 4 Feb 2022 23:16:51 +0530 Subject: [PATCH 451/460] target/riscv: Implement AIA IMSIC interface CSRs The AIA specification defines IMSIC interface CSRs for easy access to the per-HART IMSIC registers without using indirect xiselect and xireg CSRs. This patch implements the AIA IMSIC interface CSRs. Signed-off-by: Anup Patel Signed-off-by: Anup Patel Reviewed-by: Frank Chang Message-id: 20220204174700.534953-16-anup@brainfault.org Signed-off-by: Alistair Francis --- target/riscv/csr.c | 203 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 203 insertions(+) diff --git a/target/riscv/csr.c b/target/riscv/csr.c index a186b31fcf..fe2c8dd40e 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -942,6 +942,16 @@ static int aia_xlate_vs_csrno(CPURISCVState *env, int csrno) return CSR_VSISELECT; case CSR_SIREG: return CSR_VSIREG; + case CSR_SSETEIPNUM: + return CSR_VSSETEIPNUM; + case CSR_SCLREIPNUM: + return CSR_VSCLREIPNUM; + case CSR_SSETEIENUM: + return CSR_VSSETEIENUM; + case CSR_SCLREIENUM: + return CSR_VSCLREIENUM; + case CSR_STOPEI: + return CSR_VSTOPEI; default: return csrno; }; @@ -1094,6 +1104,178 @@ done: return RISCV_EXCP_NONE; } +static int rmw_xsetclreinum(CPURISCVState *env, int csrno, target_ulong *val, + target_ulong new_val, target_ulong wr_mask) +{ + int ret = -EINVAL; + bool set, pend, virt; + target_ulong priv, isel, vgein, xlen, nval, wmask; + + /* Translate CSR number for VS-mode */ + csrno = aia_xlate_vs_csrno(env, csrno); + + /* Decode register details from CSR number */ + virt = set = pend = false; + switch (csrno) { + case CSR_MSETEIPNUM: + priv = PRV_M; + set = true; + pend = true; + break; + case CSR_MCLREIPNUM: + priv = PRV_M; + pend = true; + break; + case CSR_MSETEIENUM: + priv = PRV_M; + set = true; + break; + case CSR_MCLREIENUM: + priv = PRV_M; + break; + case CSR_SSETEIPNUM: + priv = PRV_S; + set = true; + pend = true; + break; + case CSR_SCLREIPNUM: + priv = PRV_S; + pend = true; + break; + case CSR_SSETEIENUM: + priv = PRV_S; + set = true; + break; + case CSR_SCLREIENUM: + priv = PRV_S; + break; + case CSR_VSSETEIPNUM: + priv = PRV_S; + virt = true; + set = true; + pend = true; + break; + case CSR_VSCLREIPNUM: + priv = PRV_S; + virt = true; + pend = true; + break; + case CSR_VSSETEIENUM: + priv = PRV_S; + virt = true; + set = true; + break; + case CSR_VSCLREIENUM: + priv = PRV_S; + virt = true; + break; + default: + goto done; + }; + + /* IMSIC CSRs only available when machine implements IMSIC. */ + if (!env->aia_ireg_rmw_fn[priv]) { + goto done; + } + + /* Find the selected guest interrupt file */ + vgein = (virt) ? get_field(env->hstatus, HSTATUS_VGEIN) : 0; + + /* Selected guest interrupt file should be valid */ + if (virt && (!vgein || env->geilen < vgein)) { + goto done; + } + + /* Set/Clear CSRs always read zero */ + if (val) { + *val = 0; + } + + if (wr_mask) { + /* Get interrupt number */ + new_val &= wr_mask; + + /* Find target interrupt pending/enable register */ + xlen = riscv_cpu_mxl_bits(env); + isel = (new_val / xlen); + isel *= (xlen / IMSIC_EIPx_BITS); + isel += (pend) ? ISELECT_IMSIC_EIP0 : ISELECT_IMSIC_EIE0; + + /* Find the interrupt bit to be set/clear */ + wmask = ((target_ulong)1) << (new_val % xlen); + nval = (set) ? wmask : 0; + + /* Call machine specific IMSIC register emulation */ + ret = env->aia_ireg_rmw_fn[priv](env->aia_ireg_rmw_fn_arg[priv], + AIA_MAKE_IREG(isel, priv, virt, + vgein, xlen), + NULL, nval, wmask); + } else { + ret = 0; + } + +done: + if (ret) { + return (riscv_cpu_virt_enabled(env) && virt) ? + RISCV_EXCP_VIRT_INSTRUCTION_FAULT : RISCV_EXCP_ILLEGAL_INST; + } + return RISCV_EXCP_NONE; +} + +static int rmw_xtopei(CPURISCVState *env, int csrno, target_ulong *val, + target_ulong new_val, target_ulong wr_mask) +{ + bool virt; + int ret = -EINVAL; + target_ulong priv, vgein; + + /* Translate CSR number for VS-mode */ + csrno = aia_xlate_vs_csrno(env, csrno); + + /* Decode register details from CSR number */ + virt = false; + switch (csrno) { + case CSR_MTOPEI: + priv = PRV_M; + break; + case CSR_STOPEI: + priv = PRV_S; + break; + case CSR_VSTOPEI: + priv = PRV_S; + virt = true; + break; + default: + goto done; + }; + + /* IMSIC CSRs only available when machine implements IMSIC. */ + if (!env->aia_ireg_rmw_fn[priv]) { + goto done; + } + + /* Find the selected guest interrupt file */ + vgein = (virt) ? get_field(env->hstatus, HSTATUS_VGEIN) : 0; + + /* Selected guest interrupt file should be valid */ + if (virt && (!vgein || env->geilen < vgein)) { + goto done; + } + + /* Call machine specific IMSIC register emulation for TOPEI */ + ret = env->aia_ireg_rmw_fn[priv](env->aia_ireg_rmw_fn_arg[priv], + AIA_MAKE_IREG(ISELECT_IMSIC_TOPEI, priv, virt, vgein, + riscv_cpu_mxl_bits(env)), + val, new_val, wr_mask); + +done: + if (ret) { + return (riscv_cpu_virt_enabled(env) && virt) ? + RISCV_EXCP_VIRT_INSTRUCTION_FAULT : RISCV_EXCP_ILLEGAL_INST; + } + return RISCV_EXCP_NONE; +} + static RISCVException read_mtvec(CPURISCVState *env, int csrno, target_ulong *val) { @@ -2930,6 +3112,13 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { /* Machine-Level Interrupts (AIA) */ [CSR_MTOPI] = { "mtopi", aia_any, read_mtopi }, + /* Machine-Level IMSIC Interface (AIA) */ + [CSR_MSETEIPNUM] = { "mseteipnum", aia_any, NULL, NULL, rmw_xsetclreinum }, + [CSR_MCLREIPNUM] = { "mclreipnum", aia_any, NULL, NULL, rmw_xsetclreinum }, + [CSR_MSETEIENUM] = { "mseteienum", aia_any, NULL, NULL, rmw_xsetclreinum }, + [CSR_MCLREIENUM] = { "mclreienum", aia_any, NULL, NULL, rmw_xsetclreinum }, + [CSR_MTOPEI] = { "mtopei", aia_any, NULL, NULL, rmw_xtopei }, + /* Virtual Interrupts for Supervisor Level (AIA) */ [CSR_MVIEN] = { "mvien", aia_any, read_zero, write_ignore }, [CSR_MVIP] = { "mvip", aia_any, read_zero, write_ignore }, @@ -2966,6 +3155,13 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { /* Supervisor-Level Interrupts (AIA) */ [CSR_STOPI] = { "stopi", aia_smode, read_stopi }, + /* Supervisor-Level IMSIC Interface (AIA) */ + [CSR_SSETEIPNUM] = { "sseteipnum", aia_smode, NULL, NULL, rmw_xsetclreinum }, + [CSR_SCLREIPNUM] = { "sclreipnum", aia_smode, NULL, NULL, rmw_xsetclreinum }, + [CSR_SSETEIENUM] = { "sseteienum", aia_smode, NULL, NULL, rmw_xsetclreinum }, + [CSR_SCLREIENUM] = { "sclreienum", aia_smode, NULL, NULL, rmw_xsetclreinum }, + [CSR_STOPEI] = { "stopei", aia_smode, NULL, NULL, rmw_xtopei }, + /* Supervisor-Level High-Half CSRs (AIA) */ [CSR_SIEH] = { "sieh", aia_smode32, NULL, NULL, rmw_sieh }, [CSR_SIPH] = { "siph", aia_smode32, NULL, NULL, rmw_siph }, @@ -3013,6 +3209,13 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { /* VS-Level Interrupts (H-extension with AIA) */ [CSR_VSTOPI] = { "vstopi", aia_hmode, read_vstopi }, + /* VS-Level IMSIC Interface (H-extension with AIA) */ + [CSR_VSSETEIPNUM] = { "vsseteipnum", aia_hmode, NULL, NULL, rmw_xsetclreinum }, + [CSR_VSCLREIPNUM] = { "vsclreipnum", aia_hmode, NULL, NULL, rmw_xsetclreinum }, + [CSR_VSSETEIENUM] = { "vsseteienum", aia_hmode, NULL, NULL, rmw_xsetclreinum }, + [CSR_VSCLREIENUM] = { "vsclreienum", aia_hmode, NULL, NULL, rmw_xsetclreinum }, + [CSR_VSTOPEI] = { "vstopei", aia_hmode, NULL, NULL, rmw_xtopei }, + /* Hypervisor and VS-Level High-Half CSRs (H-extension with AIA) */ [CSR_HIDELEGH] = { "hidelegh", aia_hmode32, NULL, NULL, rmw_hidelegh }, [CSR_HVIENH] = { "hvienh", aia_hmode32, read_zero, write_ignore }, From d207863cd3ed056055e2324a4abe47f54e7c6384 Mon Sep 17 00:00:00 2001 From: Anup Patel Date: Fri, 4 Feb 2022 23:16:52 +0530 Subject: [PATCH 452/460] hw/riscv: virt: Use AIA INTC compatible string when available We should use the AIA INTC compatible string in the CPU INTC DT nodes when the CPUs support AIA feature. This will allow Linux INTC driver to use AIA local interrupt CSRs. Signed-off-by: Anup Patel Signed-off-by: Anup Patel Reviewed-by: Alistair Francis Reviewed-by: Frank Chang Message-id: 20220204174700.534953-17-anup@brainfault.org Signed-off-by: Alistair Francis --- hw/riscv/virt.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index 2643c8bc37..e3068d6126 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -212,8 +212,17 @@ static void create_fdt_socket_cpus(RISCVVirtState *s, int socket, qemu_fdt_add_subnode(mc->fdt, intc_name); qemu_fdt_setprop_cell(mc->fdt, intc_name, "phandle", intc_phandles[cpu]); - qemu_fdt_setprop_string(mc->fdt, intc_name, "compatible", - "riscv,cpu-intc"); + if (riscv_feature(&s->soc[socket].harts[cpu].env, + RISCV_FEATURE_AIA)) { + static const char * const compat[2] = { + "riscv,cpu-intc-aia", "riscv,cpu-intc" + }; + qemu_fdt_setprop_string_array(mc->fdt, intc_name, "compatible", + (char **)&compat, ARRAY_SIZE(compat)); + } else { + qemu_fdt_setprop_string(mc->fdt, intc_name, "compatible", + "riscv,cpu-intc"); + } qemu_fdt_setprop(mc->fdt, intc_name, "interrupt-controller", NULL, 0); qemu_fdt_setprop_cell(mc->fdt, intc_name, "#interrupt-cells", 1); From 91870b510ae5d1cb9688231b8f01dceaab64de68 Mon Sep 17 00:00:00 2001 From: Anup Patel Date: Fri, 4 Feb 2022 23:16:53 +0530 Subject: [PATCH 453/460] target/riscv: Allow users to force enable AIA CSRs in HART We add "x-aia" command-line option for RISC-V HART using which allows users to force enable CPU AIA CSRs without changing the interrupt controller available in RISC-V machine. Signed-off-by: Anup Patel Signed-off-by: Anup Patel Reviewed-by: Alistair Francis Reviewed-by: Frank Chang Message-id: 20220204174700.534953-18-anup@brainfault.org Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 5 +++++ target/riscv/cpu.h | 1 + 2 files changed, 6 insertions(+) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 5fb0a61036..9dce57a380 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -537,6 +537,10 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp) } } + if (cpu->cfg.aia) { + riscv_set_feature(env, RISCV_FEATURE_AIA); + } + set_resetvec(env, cpu->cfg.resetvec); /* Validate that MISA_MXL is set properly. */ @@ -782,6 +786,7 @@ static Property riscv_cpu_properties[] = { DEFINE_PROP_BOOL("x-j", RISCVCPU, cfg.ext_j, false), /* ePMP 0.9.3 */ DEFINE_PROP_BOOL("x-epmp", RISCVCPU, cfg.epmp, false), + DEFINE_PROP_BOOL("x-aia", RISCVCPU, cfg.aia, false), DEFINE_PROP_UINT64("resetvec", RISCVCPU, cfg.resetvec, DEFAULT_RSTVEC), DEFINE_PROP_END_OF_LIST(), diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index c70de10c85..7ecb1387dd 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -376,6 +376,7 @@ struct RISCVCPUConfig { bool mmu; bool pmp; bool epmp; + bool aia; uint64_t resetvec; }; From e8f79343cfc886aaa225cec9faf6881f75945209 Mon Sep 17 00:00:00 2001 From: Anup Patel Date: Fri, 4 Feb 2022 23:16:54 +0530 Subject: [PATCH 454/460] hw/intc: Add RISC-V AIA APLIC device emulation The RISC-V AIA (Advanced Interrupt Architecture) defines a new interrupt controller for wired interrupts called APLIC (Advanced Platform Level Interrupt Controller). The APLIC is capabable of forwarding wired interupts to RISC-V HARTs directly or as MSIs (Message Signaled Interupts). This patch adds device emulation for RISC-V AIA APLIC. Signed-off-by: Anup Patel Signed-off-by: Anup Patel Reviewed-by: Frank Chang Message-id: 20220204174700.534953-19-anup@brainfault.org Signed-off-by: Alistair Francis --- hw/intc/Kconfig | 3 + hw/intc/meson.build | 1 + hw/intc/riscv_aplic.c | 978 ++++++++++++++++++++++++++++++++++ include/hw/intc/riscv_aplic.h | 79 +++ 4 files changed, 1061 insertions(+) create mode 100644 hw/intc/riscv_aplic.c create mode 100644 include/hw/intc/riscv_aplic.h diff --git a/hw/intc/Kconfig b/hw/intc/Kconfig index 010ded7eae..528e77b4a6 100644 --- a/hw/intc/Kconfig +++ b/hw/intc/Kconfig @@ -70,6 +70,9 @@ config LOONGSON_LIOINTC config RISCV_ACLINT bool +config RISCV_APLIC + bool + config SIFIVE_PLIC bool diff --git a/hw/intc/meson.build b/hw/intc/meson.build index 70080bc161..7466024402 100644 --- a/hw/intc/meson.build +++ b/hw/intc/meson.build @@ -50,6 +50,7 @@ specific_ss.add(when: 'CONFIG_S390_FLIC', if_true: files('s390_flic.c')) specific_ss.add(when: 'CONFIG_S390_FLIC_KVM', if_true: files('s390_flic_kvm.c')) specific_ss.add(when: 'CONFIG_SH_INTC', if_true: files('sh_intc.c')) specific_ss.add(when: 'CONFIG_RISCV_ACLINT', if_true: files('riscv_aclint.c')) +specific_ss.add(when: 'CONFIG_RISCV_APLIC', if_true: files('riscv_aplic.c')) specific_ss.add(when: 'CONFIG_SIFIVE_PLIC', if_true: files('sifive_plic.c')) specific_ss.add(when: 'CONFIG_XICS', if_true: files('xics.c')) specific_ss.add(when: ['CONFIG_KVM', 'CONFIG_XICS'], diff --git a/hw/intc/riscv_aplic.c b/hw/intc/riscv_aplic.c new file mode 100644 index 0000000000..e7809fb6b2 --- /dev/null +++ b/hw/intc/riscv_aplic.c @@ -0,0 +1,978 @@ +/* + * RISC-V APLIC (Advanced Platform Level Interrupt Controller) + * + * Copyright (c) 2021 Western Digital Corporation or its affiliates. + * + * 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 "qapi/error.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "qemu/error-report.h" +#include "qemu/bswap.h" +#include "exec/address-spaces.h" +#include "hw/sysbus.h" +#include "hw/pci/msi.h" +#include "hw/boards.h" +#include "hw/qdev-properties.h" +#include "hw/intc/riscv_aplic.h" +#include "hw/irq.h" +#include "target/riscv/cpu.h" +#include "sysemu/sysemu.h" +#include "migration/vmstate.h" + +#define APLIC_MAX_IDC (1UL << 14) +#define APLIC_MAX_SOURCE 1024 +#define APLIC_MIN_IPRIO_BITS 1 +#define APLIC_MAX_IPRIO_BITS 8 +#define APLIC_MAX_CHILDREN 1024 + +#define APLIC_DOMAINCFG 0x0000 +#define APLIC_DOMAINCFG_RDONLY 0x80000000 +#define APLIC_DOMAINCFG_IE (1 << 8) +#define APLIC_DOMAINCFG_DM (1 << 2) +#define APLIC_DOMAINCFG_BE (1 << 0) + +#define APLIC_SOURCECFG_BASE 0x0004 +#define APLIC_SOURCECFG_D (1 << 10) +#define APLIC_SOURCECFG_CHILDIDX_MASK 0x000003ff +#define APLIC_SOURCECFG_SM_MASK 0x00000007 +#define APLIC_SOURCECFG_SM_INACTIVE 0x0 +#define APLIC_SOURCECFG_SM_DETACH 0x1 +#define APLIC_SOURCECFG_SM_EDGE_RISE 0x4 +#define APLIC_SOURCECFG_SM_EDGE_FALL 0x5 +#define APLIC_SOURCECFG_SM_LEVEL_HIGH 0x6 +#define APLIC_SOURCECFG_SM_LEVEL_LOW 0x7 + +#define APLIC_MMSICFGADDR 0x1bc0 +#define APLIC_MMSICFGADDRH 0x1bc4 +#define APLIC_SMSICFGADDR 0x1bc8 +#define APLIC_SMSICFGADDRH 0x1bcc + +#define APLIC_xMSICFGADDRH_L (1UL << 31) +#define APLIC_xMSICFGADDRH_HHXS_MASK 0x1f +#define APLIC_xMSICFGADDRH_HHXS_SHIFT 24 +#define APLIC_xMSICFGADDRH_LHXS_MASK 0x7 +#define APLIC_xMSICFGADDRH_LHXS_SHIFT 20 +#define APLIC_xMSICFGADDRH_HHXW_MASK 0x7 +#define APLIC_xMSICFGADDRH_HHXW_SHIFT 16 +#define APLIC_xMSICFGADDRH_LHXW_MASK 0xf +#define APLIC_xMSICFGADDRH_LHXW_SHIFT 12 +#define APLIC_xMSICFGADDRH_BAPPN_MASK 0xfff + +#define APLIC_xMSICFGADDR_PPN_SHIFT 12 + +#define APLIC_xMSICFGADDR_PPN_HART(__lhxs) \ + ((1UL << (__lhxs)) - 1) + +#define APLIC_xMSICFGADDR_PPN_LHX_MASK(__lhxw) \ + ((1UL << (__lhxw)) - 1) +#define APLIC_xMSICFGADDR_PPN_LHX_SHIFT(__lhxs) \ + ((__lhxs)) +#define APLIC_xMSICFGADDR_PPN_LHX(__lhxw, __lhxs) \ + (APLIC_xMSICFGADDR_PPN_LHX_MASK(__lhxw) << \ + APLIC_xMSICFGADDR_PPN_LHX_SHIFT(__lhxs)) + +#define APLIC_xMSICFGADDR_PPN_HHX_MASK(__hhxw) \ + ((1UL << (__hhxw)) - 1) +#define APLIC_xMSICFGADDR_PPN_HHX_SHIFT(__hhxs) \ + ((__hhxs) + APLIC_xMSICFGADDR_PPN_SHIFT) +#define APLIC_xMSICFGADDR_PPN_HHX(__hhxw, __hhxs) \ + (APLIC_xMSICFGADDR_PPN_HHX_MASK(__hhxw) << \ + APLIC_xMSICFGADDR_PPN_HHX_SHIFT(__hhxs)) + +#define APLIC_xMSICFGADDRH_VALID_MASK \ + (APLIC_xMSICFGADDRH_L | \ + (APLIC_xMSICFGADDRH_HHXS_MASK << APLIC_xMSICFGADDRH_HHXS_SHIFT) | \ + (APLIC_xMSICFGADDRH_LHXS_MASK << APLIC_xMSICFGADDRH_LHXS_SHIFT) | \ + (APLIC_xMSICFGADDRH_HHXW_MASK << APLIC_xMSICFGADDRH_HHXW_SHIFT) | \ + (APLIC_xMSICFGADDRH_LHXW_MASK << APLIC_xMSICFGADDRH_LHXW_SHIFT) | \ + APLIC_xMSICFGADDRH_BAPPN_MASK) + +#define APLIC_SETIP_BASE 0x1c00 +#define APLIC_SETIPNUM 0x1cdc + +#define APLIC_CLRIP_BASE 0x1d00 +#define APLIC_CLRIPNUM 0x1ddc + +#define APLIC_SETIE_BASE 0x1e00 +#define APLIC_SETIENUM 0x1edc + +#define APLIC_CLRIE_BASE 0x1f00 +#define APLIC_CLRIENUM 0x1fdc + +#define APLIC_SETIPNUM_LE 0x2000 +#define APLIC_SETIPNUM_BE 0x2004 + +#define APLIC_ISTATE_PENDING (1U << 0) +#define APLIC_ISTATE_ENABLED (1U << 1) +#define APLIC_ISTATE_ENPEND (APLIC_ISTATE_ENABLED | \ + APLIC_ISTATE_PENDING) +#define APLIC_ISTATE_INPUT (1U << 8) + +#define APLIC_GENMSI 0x3000 + +#define APLIC_TARGET_BASE 0x3004 +#define APLIC_TARGET_HART_IDX_SHIFT 18 +#define APLIC_TARGET_HART_IDX_MASK 0x3fff +#define APLIC_TARGET_GUEST_IDX_SHIFT 12 +#define APLIC_TARGET_GUEST_IDX_MASK 0x3f +#define APLIC_TARGET_IPRIO_MASK 0xff +#define APLIC_TARGET_EIID_MASK 0x7ff + +#define APLIC_IDC_BASE 0x4000 +#define APLIC_IDC_SIZE 32 + +#define APLIC_IDC_IDELIVERY 0x00 + +#define APLIC_IDC_IFORCE 0x04 + +#define APLIC_IDC_ITHRESHOLD 0x08 + +#define APLIC_IDC_TOPI 0x18 +#define APLIC_IDC_TOPI_ID_SHIFT 16 +#define APLIC_IDC_TOPI_ID_MASK 0x3ff +#define APLIC_IDC_TOPI_PRIO_MASK 0xff + +#define APLIC_IDC_CLAIMI 0x1c + +static uint32_t riscv_aplic_read_input_word(RISCVAPLICState *aplic, + uint32_t word) +{ + uint32_t i, irq, ret = 0; + + for (i = 0; i < 32; i++) { + irq = word * 32 + i; + if (!irq || aplic->num_irqs <= irq) { + continue; + } + + ret |= ((aplic->state[irq] & APLIC_ISTATE_INPUT) ? 1 : 0) << i; + } + + return ret; +} + +static uint32_t riscv_aplic_read_pending_word(RISCVAPLICState *aplic, + uint32_t word) +{ + uint32_t i, irq, ret = 0; + + for (i = 0; i < 32; i++) { + irq = word * 32 + i; + if (!irq || aplic->num_irqs <= irq) { + continue; + } + + ret |= ((aplic->state[irq] & APLIC_ISTATE_PENDING) ? 1 : 0) << i; + } + + return ret; +} + +static void riscv_aplic_set_pending_raw(RISCVAPLICState *aplic, + uint32_t irq, bool pending) +{ + if (pending) { + aplic->state[irq] |= APLIC_ISTATE_PENDING; + } else { + aplic->state[irq] &= ~APLIC_ISTATE_PENDING; + } +} + +static void riscv_aplic_set_pending(RISCVAPLICState *aplic, + uint32_t irq, bool pending) +{ + uint32_t sourcecfg, sm; + + if ((irq <= 0) || (aplic->num_irqs <= irq)) { + return; + } + + sourcecfg = aplic->sourcecfg[irq]; + if (sourcecfg & APLIC_SOURCECFG_D) { + return; + } + + sm = sourcecfg & APLIC_SOURCECFG_SM_MASK; + if ((sm == APLIC_SOURCECFG_SM_INACTIVE) || + ((!aplic->msimode || (aplic->msimode && !pending)) && + ((sm == APLIC_SOURCECFG_SM_LEVEL_HIGH) || + (sm == APLIC_SOURCECFG_SM_LEVEL_LOW)))) { + return; + } + + riscv_aplic_set_pending_raw(aplic, irq, pending); +} + +static void riscv_aplic_set_pending_word(RISCVAPLICState *aplic, + uint32_t word, uint32_t value, + bool pending) +{ + uint32_t i, irq; + + for (i = 0; i < 32; i++) { + irq = word * 32 + i; + if (!irq || aplic->num_irqs <= irq) { + continue; + } + + if (value & (1U << i)) { + riscv_aplic_set_pending(aplic, irq, pending); + } + } +} + +static uint32_t riscv_aplic_read_enabled_word(RISCVAPLICState *aplic, + int word) +{ + uint32_t i, irq, ret = 0; + + for (i = 0; i < 32; i++) { + irq = word * 32 + i; + if (!irq || aplic->num_irqs <= irq) { + continue; + } + + ret |= ((aplic->state[irq] & APLIC_ISTATE_ENABLED) ? 1 : 0) << i; + } + + return ret; +} + +static void riscv_aplic_set_enabled_raw(RISCVAPLICState *aplic, + uint32_t irq, bool enabled) +{ + if (enabled) { + aplic->state[irq] |= APLIC_ISTATE_ENABLED; + } else { + aplic->state[irq] &= ~APLIC_ISTATE_ENABLED; + } +} + +static void riscv_aplic_set_enabled(RISCVAPLICState *aplic, + uint32_t irq, bool enabled) +{ + uint32_t sourcecfg, sm; + + if ((irq <= 0) || (aplic->num_irqs <= irq)) { + return; + } + + sourcecfg = aplic->sourcecfg[irq]; + if (sourcecfg & APLIC_SOURCECFG_D) { + return; + } + + sm = sourcecfg & APLIC_SOURCECFG_SM_MASK; + if (sm == APLIC_SOURCECFG_SM_INACTIVE) { + return; + } + + riscv_aplic_set_enabled_raw(aplic, irq, enabled); +} + +static void riscv_aplic_set_enabled_word(RISCVAPLICState *aplic, + uint32_t word, uint32_t value, + bool enabled) +{ + uint32_t i, irq; + + for (i = 0; i < 32; i++) { + irq = word * 32 + i; + if (!irq || aplic->num_irqs <= irq) { + continue; + } + + if (value & (1U << i)) { + riscv_aplic_set_enabled(aplic, irq, enabled); + } + } +} + +static void riscv_aplic_msi_send(RISCVAPLICState *aplic, + uint32_t hart_idx, uint32_t guest_idx, + uint32_t eiid) +{ + uint64_t addr; + MemTxResult result; + RISCVAPLICState *aplic_m; + uint32_t lhxs, lhxw, hhxs, hhxw, group_idx, msicfgaddr, msicfgaddrH; + + aplic_m = aplic; + while (aplic_m && !aplic_m->mmode) { + aplic_m = aplic_m->parent; + } + if (!aplic_m) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: m-level APLIC not found\n", + __func__); + return; + } + + if (aplic->mmode) { + msicfgaddr = aplic_m->mmsicfgaddr; + msicfgaddrH = aplic_m->mmsicfgaddrH; + } else { + msicfgaddr = aplic_m->smsicfgaddr; + msicfgaddrH = aplic_m->smsicfgaddrH; + } + + lhxs = (msicfgaddrH >> APLIC_xMSICFGADDRH_LHXS_SHIFT) & + APLIC_xMSICFGADDRH_LHXS_MASK; + lhxw = (msicfgaddrH >> APLIC_xMSICFGADDRH_LHXW_SHIFT) & + APLIC_xMSICFGADDRH_LHXW_MASK; + hhxs = (msicfgaddrH >> APLIC_xMSICFGADDRH_HHXS_SHIFT) & + APLIC_xMSICFGADDRH_HHXS_MASK; + hhxw = (msicfgaddrH >> APLIC_xMSICFGADDRH_HHXW_SHIFT) & + APLIC_xMSICFGADDRH_HHXW_MASK; + + group_idx = hart_idx >> lhxw; + hart_idx &= APLIC_xMSICFGADDR_PPN_LHX_MASK(lhxw); + + addr = msicfgaddr; + addr |= ((uint64_t)(msicfgaddrH & APLIC_xMSICFGADDRH_BAPPN_MASK)) << 32; + addr |= ((uint64_t)(group_idx & APLIC_xMSICFGADDR_PPN_HHX_MASK(hhxw))) << + APLIC_xMSICFGADDR_PPN_HHX_SHIFT(hhxs); + addr |= ((uint64_t)(hart_idx & APLIC_xMSICFGADDR_PPN_LHX_MASK(lhxw))) << + APLIC_xMSICFGADDR_PPN_LHX_SHIFT(lhxs); + addr |= (uint64_t)(guest_idx & APLIC_xMSICFGADDR_PPN_HART(lhxs)); + addr <<= APLIC_xMSICFGADDR_PPN_SHIFT; + + address_space_stl_le(&address_space_memory, addr, + eiid, MEMTXATTRS_UNSPECIFIED, &result); + if (result != MEMTX_OK) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: MSI write failed for " + "hart_index=%d guest_index=%d eiid=%d\n", + __func__, hart_idx, guest_idx, eiid); + } +} + +static void riscv_aplic_msi_irq_update(RISCVAPLICState *aplic, uint32_t irq) +{ + uint32_t hart_idx, guest_idx, eiid; + + if (!aplic->msimode || (aplic->num_irqs <= irq) || + !(aplic->domaincfg & APLIC_DOMAINCFG_IE)) { + return; + } + + if ((aplic->state[irq] & APLIC_ISTATE_ENPEND) != APLIC_ISTATE_ENPEND) { + return; + } + + riscv_aplic_set_pending_raw(aplic, irq, false); + + hart_idx = aplic->target[irq] >> APLIC_TARGET_HART_IDX_SHIFT; + hart_idx &= APLIC_TARGET_HART_IDX_MASK; + if (aplic->mmode) { + /* M-level APLIC ignores guest_index */ + guest_idx = 0; + } else { + guest_idx = aplic->target[irq] >> APLIC_TARGET_GUEST_IDX_SHIFT; + guest_idx &= APLIC_TARGET_GUEST_IDX_MASK; + } + eiid = aplic->target[irq] & APLIC_TARGET_EIID_MASK; + riscv_aplic_msi_send(aplic, hart_idx, guest_idx, eiid); +} + +static uint32_t riscv_aplic_idc_topi(RISCVAPLICState *aplic, uint32_t idc) +{ + uint32_t best_irq, best_iprio; + uint32_t irq, iprio, ihartidx, ithres; + + if (aplic->num_harts <= idc) { + return 0; + } + + ithres = aplic->ithreshold[idc]; + best_irq = best_iprio = UINT32_MAX; + for (irq = 1; irq < aplic->num_irqs; irq++) { + if ((aplic->state[irq] & APLIC_ISTATE_ENPEND) != + APLIC_ISTATE_ENPEND) { + continue; + } + + ihartidx = aplic->target[irq] >> APLIC_TARGET_HART_IDX_SHIFT; + ihartidx &= APLIC_TARGET_HART_IDX_MASK; + if (ihartidx != idc) { + continue; + } + + iprio = aplic->target[irq] & aplic->iprio_mask; + if (ithres && iprio >= ithres) { + continue; + } + + if (iprio < best_iprio) { + best_irq = irq; + best_iprio = iprio; + } + } + + if (best_irq < aplic->num_irqs && best_iprio <= aplic->iprio_mask) { + return (best_irq << APLIC_IDC_TOPI_ID_SHIFT) | best_iprio; + } + + return 0; +} + +static void riscv_aplic_idc_update(RISCVAPLICState *aplic, uint32_t idc) +{ + uint32_t topi; + + if (aplic->msimode || aplic->num_harts <= idc) { + return; + } + + topi = riscv_aplic_idc_topi(aplic, idc); + if ((aplic->domaincfg & APLIC_DOMAINCFG_IE) && + aplic->idelivery[idc] && + (aplic->iforce[idc] || topi)) { + qemu_irq_raise(aplic->external_irqs[idc]); + } else { + qemu_irq_lower(aplic->external_irqs[idc]); + } +} + +static uint32_t riscv_aplic_idc_claimi(RISCVAPLICState *aplic, uint32_t idc) +{ + uint32_t irq, state, sm, topi = riscv_aplic_idc_topi(aplic, idc); + + if (!topi) { + aplic->iforce[idc] = 0; + return 0; + } + + irq = (topi >> APLIC_IDC_TOPI_ID_SHIFT) & APLIC_IDC_TOPI_ID_MASK; + sm = aplic->sourcecfg[irq] & APLIC_SOURCECFG_SM_MASK; + state = aplic->state[irq]; + riscv_aplic_set_pending_raw(aplic, irq, false); + if ((sm == APLIC_SOURCECFG_SM_LEVEL_HIGH) && + (state & APLIC_ISTATE_INPUT)) { + riscv_aplic_set_pending_raw(aplic, irq, true); + } else if ((sm == APLIC_SOURCECFG_SM_LEVEL_LOW) && + !(state & APLIC_ISTATE_INPUT)) { + riscv_aplic_set_pending_raw(aplic, irq, true); + } + riscv_aplic_idc_update(aplic, idc); + + return topi; +} + +static void riscv_aplic_request(void *opaque, int irq, int level) +{ + bool update = false; + RISCVAPLICState *aplic = opaque; + uint32_t sourcecfg, childidx, state, idc; + + assert((0 < irq) && (irq < aplic->num_irqs)); + + sourcecfg = aplic->sourcecfg[irq]; + if (sourcecfg & APLIC_SOURCECFG_D) { + childidx = sourcecfg & APLIC_SOURCECFG_CHILDIDX_MASK; + if (childidx < aplic->num_children) { + riscv_aplic_request(aplic->children[childidx], irq, level); + } + return; + } + + state = aplic->state[irq]; + switch (sourcecfg & APLIC_SOURCECFG_SM_MASK) { + case APLIC_SOURCECFG_SM_EDGE_RISE: + if ((level > 0) && !(state & APLIC_ISTATE_INPUT) && + !(state & APLIC_ISTATE_PENDING)) { + riscv_aplic_set_pending_raw(aplic, irq, true); + update = true; + } + break; + case APLIC_SOURCECFG_SM_EDGE_FALL: + if ((level <= 0) && (state & APLIC_ISTATE_INPUT) && + !(state & APLIC_ISTATE_PENDING)) { + riscv_aplic_set_pending_raw(aplic, irq, true); + update = true; + } + break; + case APLIC_SOURCECFG_SM_LEVEL_HIGH: + if ((level > 0) && !(state & APLIC_ISTATE_PENDING)) { + riscv_aplic_set_pending_raw(aplic, irq, true); + update = true; + } + break; + case APLIC_SOURCECFG_SM_LEVEL_LOW: + if ((level <= 0) && !(state & APLIC_ISTATE_PENDING)) { + riscv_aplic_set_pending_raw(aplic, irq, true); + update = true; + } + break; + default: + break; + } + + if (level <= 0) { + aplic->state[irq] &= ~APLIC_ISTATE_INPUT; + } else { + aplic->state[irq] |= APLIC_ISTATE_INPUT; + } + + if (update) { + if (aplic->msimode) { + riscv_aplic_msi_irq_update(aplic, irq); + } else { + idc = aplic->target[irq] >> APLIC_TARGET_HART_IDX_SHIFT; + idc &= APLIC_TARGET_HART_IDX_MASK; + riscv_aplic_idc_update(aplic, idc); + } + } +} + +static uint64_t riscv_aplic_read(void *opaque, hwaddr addr, unsigned size) +{ + uint32_t irq, word, idc; + RISCVAPLICState *aplic = opaque; + + /* Reads must be 4 byte words */ + if ((addr & 0x3) != 0) { + goto err; + } + + if (addr == APLIC_DOMAINCFG) { + return APLIC_DOMAINCFG_RDONLY | aplic->domaincfg | + (aplic->msimode ? APLIC_DOMAINCFG_DM : 0); + } else if ((APLIC_SOURCECFG_BASE <= addr) && + (addr < (APLIC_SOURCECFG_BASE + (aplic->num_irqs - 1) * 4))) { + irq = ((addr - APLIC_SOURCECFG_BASE) >> 2) + 1; + return aplic->sourcecfg[irq]; + } else if (aplic->mmode && aplic->msimode && + (addr == APLIC_MMSICFGADDR)) { + return aplic->mmsicfgaddr; + } else if (aplic->mmode && aplic->msimode && + (addr == APLIC_MMSICFGADDRH)) { + return aplic->mmsicfgaddrH; + } else if (aplic->mmode && aplic->msimode && + (addr == APLIC_SMSICFGADDR)) { + /* + * Registers SMSICFGADDR and SMSICFGADDRH are implemented only if: + * (a) the interrupt domain is at machine level + * (b) the domain's harts implement supervisor mode + * (c) the domain has one or more child supervisor-level domains + * that support MSI delivery mode (domaincfg.DM is not read- + * only zero in at least one of the supervisor-level child + * domains). + */ + return (aplic->num_children) ? aplic->smsicfgaddr : 0; + } else if (aplic->mmode && aplic->msimode && + (addr == APLIC_SMSICFGADDRH)) { + return (aplic->num_children) ? aplic->smsicfgaddrH : 0; + } else if ((APLIC_SETIP_BASE <= addr) && + (addr < (APLIC_SETIP_BASE + aplic->bitfield_words * 4))) { + word = (addr - APLIC_SETIP_BASE) >> 2; + return riscv_aplic_read_pending_word(aplic, word); + } else if (addr == APLIC_SETIPNUM) { + return 0; + } else if ((APLIC_CLRIP_BASE <= addr) && + (addr < (APLIC_CLRIP_BASE + aplic->bitfield_words * 4))) { + word = (addr - APLIC_CLRIP_BASE) >> 2; + return riscv_aplic_read_input_word(aplic, word); + } else if (addr == APLIC_CLRIPNUM) { + return 0; + } else if ((APLIC_SETIE_BASE <= addr) && + (addr < (APLIC_SETIE_BASE + aplic->bitfield_words * 4))) { + word = (addr - APLIC_SETIE_BASE) >> 2; + return riscv_aplic_read_enabled_word(aplic, word); + } else if (addr == APLIC_SETIENUM) { + return 0; + } else if ((APLIC_CLRIE_BASE <= addr) && + (addr < (APLIC_CLRIE_BASE + aplic->bitfield_words * 4))) { + return 0; + } else if (addr == APLIC_CLRIENUM) { + return 0; + } else if (addr == APLIC_SETIPNUM_LE) { + return 0; + } else if (addr == APLIC_SETIPNUM_BE) { + return 0; + } else if (addr == APLIC_GENMSI) { + return (aplic->msimode) ? aplic->genmsi : 0; + } else if ((APLIC_TARGET_BASE <= addr) && + (addr < (APLIC_TARGET_BASE + (aplic->num_irqs - 1) * 4))) { + irq = ((addr - APLIC_TARGET_BASE) >> 2) + 1; + return aplic->target[irq]; + } else if (!aplic->msimode && (APLIC_IDC_BASE <= addr) && + (addr < (APLIC_IDC_BASE + aplic->num_harts * APLIC_IDC_SIZE))) { + idc = (addr - APLIC_IDC_BASE) / APLIC_IDC_SIZE; + switch (addr - (APLIC_IDC_BASE + idc * APLIC_IDC_SIZE)) { + case APLIC_IDC_IDELIVERY: + return aplic->idelivery[idc]; + case APLIC_IDC_IFORCE: + return aplic->iforce[idc]; + case APLIC_IDC_ITHRESHOLD: + return aplic->ithreshold[idc]; + case APLIC_IDC_TOPI: + return riscv_aplic_idc_topi(aplic, idc); + case APLIC_IDC_CLAIMI: + return riscv_aplic_idc_claimi(aplic, idc); + default: + goto err; + }; + } + +err: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Invalid register read 0x%" HWADDR_PRIx "\n", + __func__, addr); + return 0; +} + +static void riscv_aplic_write(void *opaque, hwaddr addr, uint64_t value, + unsigned size) +{ + RISCVAPLICState *aplic = opaque; + uint32_t irq, word, idc = UINT32_MAX; + + /* Writes must be 4 byte words */ + if ((addr & 0x3) != 0) { + goto err; + } + + if (addr == APLIC_DOMAINCFG) { + /* Only IE bit writeable at the moment */ + value &= APLIC_DOMAINCFG_IE; + aplic->domaincfg = value; + } else if ((APLIC_SOURCECFG_BASE <= addr) && + (addr < (APLIC_SOURCECFG_BASE + (aplic->num_irqs - 1) * 4))) { + irq = ((addr - APLIC_SOURCECFG_BASE) >> 2) + 1; + if (!aplic->num_children && (value & APLIC_SOURCECFG_D)) { + value = 0; + } + if (value & APLIC_SOURCECFG_D) { + value &= (APLIC_SOURCECFG_D | APLIC_SOURCECFG_CHILDIDX_MASK); + } else { + value &= (APLIC_SOURCECFG_D | APLIC_SOURCECFG_SM_MASK); + } + aplic->sourcecfg[irq] = value; + if ((aplic->sourcecfg[irq] & APLIC_SOURCECFG_D) || + (aplic->sourcecfg[irq] == 0)) { + riscv_aplic_set_pending_raw(aplic, irq, false); + riscv_aplic_set_enabled_raw(aplic, irq, false); + } + } else if (aplic->mmode && aplic->msimode && + (addr == APLIC_MMSICFGADDR)) { + if (!(aplic->mmsicfgaddrH & APLIC_xMSICFGADDRH_L)) { + aplic->mmsicfgaddr = value; + } + } else if (aplic->mmode && aplic->msimode && + (addr == APLIC_MMSICFGADDRH)) { + if (!(aplic->mmsicfgaddrH & APLIC_xMSICFGADDRH_L)) { + aplic->mmsicfgaddrH = value & APLIC_xMSICFGADDRH_VALID_MASK; + } + } else if (aplic->mmode && aplic->msimode && + (addr == APLIC_SMSICFGADDR)) { + /* + * Registers SMSICFGADDR and SMSICFGADDRH are implemented only if: + * (a) the interrupt domain is at machine level + * (b) the domain's harts implement supervisor mode + * (c) the domain has one or more child supervisor-level domains + * that support MSI delivery mode (domaincfg.DM is not read- + * only zero in at least one of the supervisor-level child + * domains). + */ + if (aplic->num_children && + !(aplic->smsicfgaddrH & APLIC_xMSICFGADDRH_L)) { + aplic->smsicfgaddr = value; + } + } else if (aplic->mmode && aplic->msimode && + (addr == APLIC_SMSICFGADDRH)) { + if (aplic->num_children && + !(aplic->smsicfgaddrH & APLIC_xMSICFGADDRH_L)) { + aplic->smsicfgaddrH = value & APLIC_xMSICFGADDRH_VALID_MASK; + } + } else if ((APLIC_SETIP_BASE <= addr) && + (addr < (APLIC_SETIP_BASE + aplic->bitfield_words * 4))) { + word = (addr - APLIC_SETIP_BASE) >> 2; + riscv_aplic_set_pending_word(aplic, word, value, true); + } else if (addr == APLIC_SETIPNUM) { + riscv_aplic_set_pending(aplic, value, true); + } else if ((APLIC_CLRIP_BASE <= addr) && + (addr < (APLIC_CLRIP_BASE + aplic->bitfield_words * 4))) { + word = (addr - APLIC_CLRIP_BASE) >> 2; + riscv_aplic_set_pending_word(aplic, word, value, false); + } else if (addr == APLIC_CLRIPNUM) { + riscv_aplic_set_pending(aplic, value, false); + } else if ((APLIC_SETIE_BASE <= addr) && + (addr < (APLIC_SETIE_BASE + aplic->bitfield_words * 4))) { + word = (addr - APLIC_SETIE_BASE) >> 2; + riscv_aplic_set_enabled_word(aplic, word, value, true); + } else if (addr == APLIC_SETIENUM) { + riscv_aplic_set_enabled(aplic, value, true); + } else if ((APLIC_CLRIE_BASE <= addr) && + (addr < (APLIC_CLRIE_BASE + aplic->bitfield_words * 4))) { + word = (addr - APLIC_CLRIE_BASE) >> 2; + riscv_aplic_set_enabled_word(aplic, word, value, false); + } else if (addr == APLIC_CLRIENUM) { + riscv_aplic_set_enabled(aplic, value, false); + } else if (addr == APLIC_SETIPNUM_LE) { + riscv_aplic_set_pending(aplic, value, true); + } else if (addr == APLIC_SETIPNUM_BE) { + riscv_aplic_set_pending(aplic, bswap32(value), true); + } else if (addr == APLIC_GENMSI) { + if (aplic->msimode) { + aplic->genmsi = value & ~(APLIC_TARGET_GUEST_IDX_MASK << + APLIC_TARGET_GUEST_IDX_SHIFT); + riscv_aplic_msi_send(aplic, + value >> APLIC_TARGET_HART_IDX_SHIFT, + 0, + value & APLIC_TARGET_EIID_MASK); + } + } else if ((APLIC_TARGET_BASE <= addr) && + (addr < (APLIC_TARGET_BASE + (aplic->num_irqs - 1) * 4))) { + irq = ((addr - APLIC_TARGET_BASE) >> 2) + 1; + if (aplic->msimode) { + aplic->target[irq] = value; + } else { + aplic->target[irq] = (value & ~APLIC_TARGET_IPRIO_MASK) | + ((value & aplic->iprio_mask) ? + (value & aplic->iprio_mask) : 1); + } + } else if (!aplic->msimode && (APLIC_IDC_BASE <= addr) && + (addr < (APLIC_IDC_BASE + aplic->num_harts * APLIC_IDC_SIZE))) { + idc = (addr - APLIC_IDC_BASE) / APLIC_IDC_SIZE; + switch (addr - (APLIC_IDC_BASE + idc * APLIC_IDC_SIZE)) { + case APLIC_IDC_IDELIVERY: + aplic->idelivery[idc] = value & 0x1; + break; + case APLIC_IDC_IFORCE: + aplic->iforce[idc] = value & 0x1; + break; + case APLIC_IDC_ITHRESHOLD: + aplic->ithreshold[idc] = value & aplic->iprio_mask; + break; + default: + goto err; + }; + } else { + goto err; + } + + if (aplic->msimode) { + for (irq = 1; irq < aplic->num_irqs; irq++) { + riscv_aplic_msi_irq_update(aplic, irq); + } + } else { + if (idc == UINT32_MAX) { + for (idc = 0; idc < aplic->num_harts; idc++) { + riscv_aplic_idc_update(aplic, idc); + } + } else { + riscv_aplic_idc_update(aplic, idc); + } + } + + return; + +err: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Invalid register write 0x%" HWADDR_PRIx "\n", + __func__, addr); +} + +static const MemoryRegionOps riscv_aplic_ops = { + .read = riscv_aplic_read, + .write = riscv_aplic_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4 + } +}; + +static void riscv_aplic_realize(DeviceState *dev, Error **errp) +{ + uint32_t i; + RISCVAPLICState *aplic = RISCV_APLIC(dev); + + aplic->bitfield_words = (aplic->num_irqs + 31) >> 5; + aplic->sourcecfg = g_new0(uint32_t, aplic->num_irqs); + aplic->state = g_new(uint32_t, aplic->num_irqs); + aplic->target = g_new0(uint32_t, aplic->num_irqs); + if (!aplic->msimode) { + for (i = 0; i < aplic->num_irqs; i++) { + aplic->target[i] = 1; + } + } + aplic->idelivery = g_new0(uint32_t, aplic->num_harts); + aplic->iforce = g_new0(uint32_t, aplic->num_harts); + aplic->ithreshold = g_new0(uint32_t, aplic->num_harts); + + memory_region_init_io(&aplic->mmio, OBJECT(dev), &riscv_aplic_ops, aplic, + TYPE_RISCV_APLIC, aplic->aperture_size); + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &aplic->mmio); + + /* + * Only root APLICs have hardware IRQ lines. All non-root APLICs + * have IRQ lines delegated by their parent APLIC. + */ + if (!aplic->parent) { + qdev_init_gpio_in(dev, riscv_aplic_request, aplic->num_irqs); + } + + /* Create output IRQ lines for non-MSI mode */ + if (!aplic->msimode) { + aplic->external_irqs = g_malloc(sizeof(qemu_irq) * aplic->num_harts); + qdev_init_gpio_out(dev, aplic->external_irqs, aplic->num_harts); + + /* Claim the CPU interrupt to be triggered by this APLIC */ + for (i = 0; i < aplic->num_harts; i++) { + RISCVCPU *cpu = RISCV_CPU(qemu_get_cpu(aplic->hartid_base + i)); + if (riscv_cpu_claim_interrupts(cpu, + (aplic->mmode) ? MIP_MEIP : MIP_SEIP) < 0) { + error_report("%s already claimed", + (aplic->mmode) ? "MEIP" : "SEIP"); + exit(1); + } + } + } + + msi_nonbroken = true; +} + +static Property riscv_aplic_properties[] = { + DEFINE_PROP_UINT32("aperture-size", RISCVAPLICState, aperture_size, 0), + DEFINE_PROP_UINT32("hartid-base", RISCVAPLICState, hartid_base, 0), + DEFINE_PROP_UINT32("num-harts", RISCVAPLICState, num_harts, 0), + DEFINE_PROP_UINT32("iprio-mask", RISCVAPLICState, iprio_mask, 0), + DEFINE_PROP_UINT32("num-irqs", RISCVAPLICState, num_irqs, 0), + DEFINE_PROP_BOOL("msimode", RISCVAPLICState, msimode, 0), + DEFINE_PROP_BOOL("mmode", RISCVAPLICState, mmode, 0), + DEFINE_PROP_END_OF_LIST(), +}; + +static const VMStateDescription vmstate_riscv_aplic = { + .name = "riscv_aplic", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(domaincfg, RISCVAPLICState), + VMSTATE_UINT32(mmsicfgaddr, RISCVAPLICState), + VMSTATE_UINT32(mmsicfgaddrH, RISCVAPLICState), + VMSTATE_UINT32(smsicfgaddr, RISCVAPLICState), + VMSTATE_UINT32(smsicfgaddrH, RISCVAPLICState), + VMSTATE_UINT32(genmsi, RISCVAPLICState), + VMSTATE_VARRAY_UINT32(sourcecfg, RISCVAPLICState, + num_irqs, 0, + vmstate_info_uint32, uint32_t), + VMSTATE_VARRAY_UINT32(state, RISCVAPLICState, + num_irqs, 0, + vmstate_info_uint32, uint32_t), + VMSTATE_VARRAY_UINT32(target, RISCVAPLICState, + num_irqs, 0, + vmstate_info_uint32, uint32_t), + VMSTATE_VARRAY_UINT32(idelivery, RISCVAPLICState, + num_harts, 0, + vmstate_info_uint32, uint32_t), + VMSTATE_VARRAY_UINT32(iforce, RISCVAPLICState, + num_harts, 0, + vmstate_info_uint32, uint32_t), + VMSTATE_VARRAY_UINT32(ithreshold, RISCVAPLICState, + num_harts, 0, + vmstate_info_uint32, uint32_t), + VMSTATE_END_OF_LIST() + } +}; + +static void riscv_aplic_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + device_class_set_props(dc, riscv_aplic_properties); + dc->realize = riscv_aplic_realize; + dc->vmsd = &vmstate_riscv_aplic; +} + +static const TypeInfo riscv_aplic_info = { + .name = TYPE_RISCV_APLIC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(RISCVAPLICState), + .class_init = riscv_aplic_class_init, +}; + +static void riscv_aplic_register_types(void) +{ + type_register_static(&riscv_aplic_info); +} + +type_init(riscv_aplic_register_types) + +/* + * Add a APLIC device to another APLIC device as child for + * interrupt delegation. + */ +void riscv_aplic_add_child(DeviceState *parent, DeviceState *child) +{ + RISCVAPLICState *caplic, *paplic; + + assert(parent && child); + caplic = RISCV_APLIC(child); + paplic = RISCV_APLIC(parent); + + assert(paplic->num_irqs == caplic->num_irqs); + assert(paplic->num_children <= QEMU_APLIC_MAX_CHILDREN); + + caplic->parent = paplic; + paplic->children[paplic->num_children] = caplic; + paplic->num_children++; +} + +/* + * Create APLIC device. + */ +DeviceState *riscv_aplic_create(hwaddr addr, hwaddr size, + uint32_t hartid_base, uint32_t num_harts, uint32_t num_sources, + uint32_t iprio_bits, bool msimode, bool mmode, DeviceState *parent) +{ + DeviceState *dev = qdev_new(TYPE_RISCV_APLIC); + uint32_t i; + + assert(num_harts < APLIC_MAX_IDC); + assert((APLIC_IDC_BASE + (num_harts * APLIC_IDC_SIZE)) <= size); + assert(num_sources < APLIC_MAX_SOURCE); + assert(APLIC_MIN_IPRIO_BITS <= iprio_bits); + assert(iprio_bits <= APLIC_MAX_IPRIO_BITS); + + qdev_prop_set_uint32(dev, "aperture-size", size); + qdev_prop_set_uint32(dev, "hartid-base", hartid_base); + qdev_prop_set_uint32(dev, "num-harts", num_harts); + qdev_prop_set_uint32(dev, "iprio-mask", ((1U << iprio_bits) - 1)); + qdev_prop_set_uint32(dev, "num-irqs", num_sources + 1); + qdev_prop_set_bit(dev, "msimode", msimode); + qdev_prop_set_bit(dev, "mmode", mmode); + + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); + 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 = qemu_get_cpu(hartid_base + i); + + qdev_connect_gpio_out_named(dev, NULL, i, + qdev_get_gpio_in(DEVICE(cpu), + (mmode) ? IRQ_M_EXT : IRQ_S_EXT)); + } + } + + return dev; +} diff --git a/include/hw/intc/riscv_aplic.h b/include/hw/intc/riscv_aplic.h new file mode 100644 index 0000000000..de8532fbc3 --- /dev/null +++ b/include/hw/intc/riscv_aplic.h @@ -0,0 +1,79 @@ +/* + * RISC-V APLIC (Advanced Platform Level Interrupt Controller) interface + * + * Copyright (c) 2021 Western Digital Corporation or its affiliates. + * + * 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 . + */ + +#ifndef HW_RISCV_APLIC_H +#define HW_RISCV_APLIC_H + +#include "hw/sysbus.h" +#include "qom/object.h" + +#define TYPE_RISCV_APLIC "riscv.aplic" + +typedef struct RISCVAPLICState RISCVAPLICState; +DECLARE_INSTANCE_CHECKER(RISCVAPLICState, RISCV_APLIC, TYPE_RISCV_APLIC) + +#define APLIC_MIN_SIZE 0x4000 +#define APLIC_SIZE_ALIGN(__x) (((__x) + (APLIC_MIN_SIZE - 1)) & \ + ~(APLIC_MIN_SIZE - 1)) +#define APLIC_SIZE(__num_harts) (APLIC_MIN_SIZE + \ + APLIC_SIZE_ALIGN(32 * (__num_harts))) + +struct RISCVAPLICState { + /*< private >*/ + SysBusDevice parent_obj; + qemu_irq *external_irqs; + + /*< public >*/ + MemoryRegion mmio; + uint32_t bitfield_words; + uint32_t domaincfg; + uint32_t mmsicfgaddr; + uint32_t mmsicfgaddrH; + uint32_t smsicfgaddr; + uint32_t smsicfgaddrH; + uint32_t genmsi; + uint32_t *sourcecfg; + uint32_t *state; + uint32_t *target; + uint32_t *idelivery; + uint32_t *iforce; + uint32_t *ithreshold; + + /* topology */ +#define QEMU_APLIC_MAX_CHILDREN 16 + struct RISCVAPLICState *parent; + struct RISCVAPLICState *children[QEMU_APLIC_MAX_CHILDREN]; + uint16_t num_children; + + /* config */ + uint32_t aperture_size; + uint32_t hartid_base; + uint32_t num_harts; + uint32_t iprio_mask; + uint32_t num_irqs; + bool msimode; + bool mmode; +}; + +void riscv_aplic_add_child(DeviceState *parent, DeviceState *child); + +DeviceState *riscv_aplic_create(hwaddr addr, hwaddr size, + uint32_t hartid_base, uint32_t num_harts, uint32_t num_sources, + uint32_t iprio_bits, bool msimode, bool mmode, DeviceState *parent); + +#endif From 05e6ca5e156d1d114d1eb878cae9744cb4a539e3 Mon Sep 17 00:00:00 2001 From: Guo Ren Date: Fri, 4 Feb 2022 10:26:54 +0800 Subject: [PATCH 455/460] target/riscv: Ignore reserved bits in PTE for RV64 Highest bits of PTE has been used for svpbmt, ref: [1], [2], so we need to ignore them. They cannot be a part of ppn. 1: The RISC-V Instruction Set Manual, Volume II: Privileged Architecture 4.4 Sv39: Page-Based 39-bit Virtual-Memory System 4.5 Sv48: Page-Based 48-bit Virtual-Memory System 2: https://github.com/riscv/virtual-memory/blob/main/specs/663-Svpbmt-diff.pdf Signed-off-by: Guo Ren Reviewed-by: Liu Zhiwei Reviewed-by: Alistair Francis Cc: Bin Meng Reviewed-by: Alistair Francis Message-Id: <20220204022658.18097-2-liweiwei@iscas.ac.cn> Signed-off-by: Alistair Francis --- target/riscv/cpu.h | 15 +++++++++++++++ target/riscv/cpu_bits.h | 3 +++ target/riscv/cpu_helper.c | 13 ++++++++++++- 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 7ecb1387dd..cefccb4016 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -359,6 +359,8 @@ struct RISCVCPUConfig { bool ext_counters; bool ext_ifencei; bool ext_icsr; + bool ext_svnapot; + bool ext_svpbmt; bool ext_zfh; bool ext_zfhmin; bool ext_zve32f; @@ -558,6 +560,19 @@ static inline int riscv_cpu_xlen(CPURISCVState *env) return 16 << env->xl; } +#ifdef TARGET_RISCV32 +#define riscv_cpu_sxl(env) ((void)(env), MXL_RV32) +#else +static inline RISCVMXL riscv_cpu_sxl(CPURISCVState *env) +{ +#ifdef CONFIG_USER_ONLY + return env->misa_mxl; +#else + return get_field(env->mstatus, MSTATUS64_SXL); +#endif +} +#endif + /* * Encode LMUL to lmul as follows: * LMUL vlmul lmul diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h index 068c4d8034..b3489cbc10 100644 --- a/target/riscv/cpu_bits.h +++ b/target/riscv/cpu_bits.h @@ -565,6 +565,9 @@ typedef enum { /* Page table PPN shift amount */ #define PTE_PPN_SHIFT 10 +/* Page table PPN mask */ +#define PTE_PPN_MASK 0x3FFFFFFFFFFC00ULL + /* Leaf page shift amount */ #define PGSHIFT 12 diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 430060dcd8..7df4569526 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -751,6 +751,8 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical, MemTxAttrs attrs = MEMTXATTRS_UNSPECIFIED; int mode = mmu_idx & TB_FLAGS_PRIV_MMU_MASK; bool use_background = false; + hwaddr ppn; + RISCVCPU *cpu = env_archcpu(env); /* * Check if we should use the background registers for the two @@ -919,7 +921,16 @@ restart: return TRANSLATE_FAIL; } - hwaddr ppn = pte >> PTE_PPN_SHIFT; + if (riscv_cpu_sxl(env) == MXL_RV32) { + ppn = pte >> PTE_PPN_SHIFT; + } else if (cpu->cfg.ext_svpbmt || cpu->cfg.ext_svnapot) { + ppn = (pte & (target_ulong)PTE_PPN_MASK) >> PTE_PPN_SHIFT; + } else { + ppn = pte >> PTE_PPN_SHIFT; + if ((pte & ~(target_ulong)PTE_PPN_MASK) >> PTE_PPN_SHIFT) { + return TRANSLATE_FAIL; + } + } if (!(pte & PTE_V)) { /* Invalid PTE */ From b6ecc63c569bb88c0fcadf79fb92bf4b88aefea8 Mon Sep 17 00:00:00 2001 From: Weiwei Li Date: Fri, 4 Feb 2022 10:26:55 +0800 Subject: [PATCH 456/460] target/riscv: add PTE_A/PTE_D/PTE_U bits check for inner PTE For non-leaf PTEs, the D, A, and U bits are reserved for future standard use. Signed-off-by: Weiwei Li Signed-off-by: Junqiang Wang Reviewed-by: Anup Patel Reviewed-by: Alistair Francis Message-Id: <20220204022658.18097-3-liweiwei@iscas.ac.cn> Signed-off-by: Alistair Francis --- target/riscv/cpu_helper.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 7df4569526..25ebc76725 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -937,6 +937,9 @@ restart: return TRANSLATE_FAIL; } else if (!(pte & (PTE_R | PTE_W | PTE_X))) { /* Inner PTE, continue walking */ + if (pte & (PTE_D | PTE_A | PTE_U)) { + return TRANSLATE_FAIL; + } base = ppn << PGSHIFT; } else if ((pte & (PTE_R | PTE_W | PTE_X)) == PTE_W) { /* Reserved leaf PTE flags: PTE_W */ From 2bacb22446a45b07f542d32b6d760da757233b20 Mon Sep 17 00:00:00 2001 From: Weiwei Li Date: Fri, 4 Feb 2022 10:26:56 +0800 Subject: [PATCH 457/460] target/riscv: add support for svnapot extension - add PTE_N bit - add PTE_N bit check for inner PTE - update address translation to support 64KiB continuous region (napot_bits = 4) Signed-off-by: Weiwei Li Signed-off-by: Junqiang Wang Reviewed-by: Anup Patel Reviewed-by: Alistair Francis Message-Id: <20220204022658.18097-4-liweiwei@iscas.ac.cn> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 2 ++ target/riscv/cpu_bits.h | 1 + target/riscv/cpu_helper.c | 18 +++++++++++++++--- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 9dce57a380..fda99c2a81 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -774,6 +774,8 @@ static Property riscv_cpu_properties[] = { DEFINE_PROP_UINT16("vlen", RISCVCPU, cfg.vlen, 128), DEFINE_PROP_UINT16("elen", RISCVCPU, cfg.elen, 64), + DEFINE_PROP_BOOL("svnapot", RISCVCPU, cfg.ext_svnapot, false), + DEFINE_PROP_BOOL("zba", RISCVCPU, cfg.ext_zba, true), DEFINE_PROP_BOOL("zbb", RISCVCPU, cfg.ext_zbb, true), DEFINE_PROP_BOOL("zbc", RISCVCPU, cfg.ext_zbc, true), diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h index b3489cbc10..37ed4da72c 100644 --- a/target/riscv/cpu_bits.h +++ b/target/riscv/cpu_bits.h @@ -561,6 +561,7 @@ typedef enum { #define PTE_A 0x040 /* Accessed */ #define PTE_D 0x080 /* Dirty */ #define PTE_SOFT 0x300 /* Reserved for Software */ +#define PTE_N 0x8000000000000000ULL /* NAPOT translation */ /* Page table PPN shift amount */ #define PTE_PPN_SHIFT 10 diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 25ebc76725..437c9488a6 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -753,6 +753,8 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical, bool use_background = false; hwaddr ppn; RISCVCPU *cpu = env_archcpu(env); + int napot_bits = 0; + target_ulong napot_mask; /* * Check if we should use the background registers for the two @@ -937,7 +939,7 @@ restart: return TRANSLATE_FAIL; } else if (!(pte & (PTE_R | PTE_W | PTE_X))) { /* Inner PTE, continue walking */ - if (pte & (PTE_D | PTE_A | PTE_U)) { + if (pte & (PTE_D | PTE_A | PTE_U | PTE_N)) { return TRANSLATE_FAIL; } base = ppn << PGSHIFT; @@ -1013,8 +1015,18 @@ restart: /* for superpage mappings, make a fake leaf PTE for the TLB's benefit. */ target_ulong vpn = addr >> PGSHIFT; - *physical = ((ppn | (vpn & ((1L << ptshift) - 1))) << PGSHIFT) | - (addr & ~TARGET_PAGE_MASK); + + if (cpu->cfg.ext_svnapot && (pte & PTE_N)) { + napot_bits = ctzl(ppn) + 1; + if ((i != (levels - 1)) || (napot_bits != 4)) { + return TRANSLATE_FAIL; + } + } + + napot_mask = (1 << napot_bits) - 1; + *physical = (((ppn & ~napot_mask) | (vpn & napot_mask) | + (vpn & (((target_ulong)1 << ptshift) - 1)) + ) << PGSHIFT) | (addr & ~TARGET_PAGE_MASK); /* set permissions on the TLB entry */ if ((pte & PTE_R) || ((pte & PTE_X) && mxr)) { From c5d77ddd8ebcd33da9561982e29c8f4b2dec0978 Mon Sep 17 00:00:00 2001 From: Weiwei Li Date: Fri, 4 Feb 2022 10:26:57 +0800 Subject: [PATCH 458/460] target/riscv: add support for svinval extension - sinval.vma, hinval.vvma and hinval.gvma do the same as sfence.vma, hfence.vvma and hfence.gvma except extension check - do nothing other than extension check for sfence.w.inval and sfence.inval.ir Signed-off-by: Weiwei Li Signed-off-by: Junqiang Wang Reviewed-by: Anup Patel Reviewed-by: Alistair Francis Message-Id: <20220204022658.18097-5-liweiwei@iscas.ac.cn> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 1 + target/riscv/cpu.h | 1 + target/riscv/insn32.decode | 7 ++ target/riscv/insn_trans/trans_svinval.c.inc | 75 +++++++++++++++++++++ target/riscv/translate.c | 1 + 5 files changed, 85 insertions(+) create mode 100644 target/riscv/insn_trans/trans_svinval.c.inc diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index fda99c2a81..e5676b40d1 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -774,6 +774,7 @@ static Property riscv_cpu_properties[] = { DEFINE_PROP_UINT16("vlen", RISCVCPU, cfg.vlen, 128), DEFINE_PROP_UINT16("elen", RISCVCPU, cfg.elen, 64), + DEFINE_PROP_BOOL("svinval", RISCVCPU, cfg.ext_svinval, false), DEFINE_PROP_BOOL("svnapot", RISCVCPU, cfg.ext_svnapot, false), DEFINE_PROP_BOOL("zba", RISCVCPU, cfg.ext_zba, true), diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index cefccb4016..8183fb86d5 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -359,6 +359,7 @@ struct RISCVCPUConfig { bool ext_counters; bool ext_ifencei; bool ext_icsr; + bool ext_svinval; bool ext_svnapot; bool ext_svpbmt; bool ext_zfh; diff --git a/target/riscv/insn32.decode b/target/riscv/insn32.decode index 5bbedc254c..1d3ff1efe1 100644 --- a/target/riscv/insn32.decode +++ b/target/riscv/insn32.decode @@ -809,3 +809,10 @@ fcvt_l_h 1100010 00010 ..... ... ..... 1010011 @r2_rm fcvt_lu_h 1100010 00011 ..... ... ..... 1010011 @r2_rm fcvt_h_l 1101010 00010 ..... ... ..... 1010011 @r2_rm fcvt_h_lu 1101010 00011 ..... ... ..... 1010011 @r2_rm + +# *** Svinval Standard Extension *** +sinval_vma 0001011 ..... ..... 000 00000 1110011 @sfence_vma +sfence_w_inval 0001100 00000 00000 000 00000 1110011 +sfence_inval_ir 0001100 00001 00000 000 00000 1110011 +hinval_vvma 0010011 ..... ..... 000 00000 1110011 @hfence_vvma +hinval_gvma 0110011 ..... ..... 000 00000 1110011 @hfence_gvma diff --git a/target/riscv/insn_trans/trans_svinval.c.inc b/target/riscv/insn_trans/trans_svinval.c.inc new file mode 100644 index 0000000000..2682bd969f --- /dev/null +++ b/target/riscv/insn_trans/trans_svinval.c.inc @@ -0,0 +1,75 @@ +/* + * RISC-V translation routines for the Svinval Standard Instruction Set. + * + * Copyright (c) 2020-2022 PLCT lab + * + * 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 . + */ + +#define REQUIRE_SVINVAL(ctx) do { \ + if (!ctx->cfg_ptr->ext_svinval) { \ + return false; \ + } \ +} while (0) + +static bool trans_sinval_vma(DisasContext *ctx, arg_sinval_vma *a) +{ + REQUIRE_SVINVAL(ctx); + /* Do the same as sfence.vma currently */ + REQUIRE_EXT(ctx, RVS); +#ifndef CONFIG_USER_ONLY + gen_helper_tlb_flush(cpu_env); + return true; +#endif + return false; +} + +static bool trans_sfence_w_inval(DisasContext *ctx, arg_sfence_w_inval *a) +{ + REQUIRE_SVINVAL(ctx); + REQUIRE_EXT(ctx, RVS); + /* Do nothing currently */ + return true; +} + +static bool trans_sfence_inval_ir(DisasContext *ctx, arg_sfence_inval_ir *a) +{ + REQUIRE_SVINVAL(ctx); + REQUIRE_EXT(ctx, RVS); + /* Do nothing currently */ + return true; +} + +static bool trans_hinval_vvma(DisasContext *ctx, arg_hinval_vvma *a) +{ + REQUIRE_SVINVAL(ctx); + /* Do the same as hfence.vvma currently */ + REQUIRE_EXT(ctx, RVH); +#ifndef CONFIG_USER_ONLY + gen_helper_hyp_tlb_flush(cpu_env); + return true; +#endif + return false; +} + +static bool trans_hinval_gvma(DisasContext *ctx, arg_hinval_gvma *a) +{ + REQUIRE_SVINVAL(ctx); + /* Do the same as hfence.gvma currently */ + REQUIRE_EXT(ctx, RVH); +#ifndef CONFIG_USER_ONLY + gen_helper_hyp_gvma_tlb_flush(cpu_env); + return true; +#endif + return false; +} diff --git a/target/riscv/translate.c b/target/riscv/translate.c index eaf5a72c81..84dbfa6340 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -862,6 +862,7 @@ static uint32_t opcode_at(DisasContextBase *dcbase, target_ulong pc) #include "insn_trans/trans_rvb.c.inc" #include "insn_trans/trans_rvzfh.c.inc" #include "insn_trans/trans_privileged.c.inc" +#include "insn_trans/trans_svinval.c.inc" #include "insn_trans/trans_xventanacondops.c.inc" /* Include the auto-generated decoder for 16 bit insn */ From bbce8ba8e6bddcd77abef4810a9426bad9939f3b Mon Sep 17 00:00:00 2001 From: Weiwei Li Date: Fri, 4 Feb 2022 10:26:58 +0800 Subject: [PATCH 459/460] target/riscv: add support for svpbmt extension - add PTE_PBMT bits: It uses two PTE bits, but otherwise has no effect on QEMU, since QEMU is sequentially consistent and doesn't model PMAs currently - add PTE_PBMT bit check for inner PTE Signed-off-by: Weiwei Li Signed-off-by: Junqiang Wang Reviewed-by: Anup Patel Reviewed-by: Alistair Francis Message-Id: <20220204022658.18097-6-liweiwei@iscas.ac.cn> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 1 + target/riscv/cpu_bits.h | 2 ++ target/riscv/cpu_helper.c | 4 +++- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index e5676b40d1..b0a40b83e7 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -776,6 +776,7 @@ static Property riscv_cpu_properties[] = { DEFINE_PROP_BOOL("svinval", RISCVCPU, cfg.ext_svinval, false), DEFINE_PROP_BOOL("svnapot", RISCVCPU, cfg.ext_svnapot, false), + DEFINE_PROP_BOOL("svpbmt", RISCVCPU, cfg.ext_svpbmt, false), DEFINE_PROP_BOOL("zba", RISCVCPU, cfg.ext_zba, true), DEFINE_PROP_BOOL("zbb", RISCVCPU, cfg.ext_zbb, true), diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h index 37ed4da72c..0fe01d7da5 100644 --- a/target/riscv/cpu_bits.h +++ b/target/riscv/cpu_bits.h @@ -561,7 +561,9 @@ typedef enum { #define PTE_A 0x040 /* Accessed */ #define PTE_D 0x080 /* Dirty */ #define PTE_SOFT 0x300 /* Reserved for Software */ +#define PTE_PBMT 0x6000000000000000ULL /* Page-based memory types */ #define PTE_N 0x8000000000000000ULL /* NAPOT translation */ +#define PTE_ATTR (PTE_N | PTE_PBMT) /* All attributes bits */ /* Page table PPN shift amount */ #define PTE_PPN_SHIFT 10 diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 437c9488a6..746335bfd6 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -937,9 +937,11 @@ restart: if (!(pte & PTE_V)) { /* Invalid PTE */ return TRANSLATE_FAIL; + } else if (!cpu->cfg.ext_svpbmt && (pte & PTE_PBMT)) { + return TRANSLATE_FAIL; } else if (!(pte & (PTE_R | PTE_W | PTE_X))) { /* Inner PTE, continue walking */ - if (pte & (PTE_D | PTE_A | PTE_U | PTE_N)) { + if (pte & (PTE_D | PTE_A | PTE_U | PTE_ATTR)) { return TRANSLATE_FAIL; } base = ppn << PGSHIFT; From 7035b8420fa52e8c94cf4317c0f88c1b73ced28d Mon Sep 17 00:00:00 2001 From: Yu Li Date: Tue, 8 Feb 2022 21:07:23 +0800 Subject: [PATCH 460/460] docs/system: riscv: Update description of CPU Since the hypervisor extension been non experimental and enabled for default CPU, the previous command is no longer available and the option `x-h=true` or `h=true` is also no longer required. Signed-off-by: Yu Li Reviewed-by: Alistair Francis Message-Id: <9040401e-8f87-ef4a-d840-6703f08d068c@bytedance.com> Signed-off-by: Alistair Francis --- docs/system/riscv/virt.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/system/riscv/virt.rst b/docs/system/riscv/virt.rst index fa016584bf..08ce3c4177 100644 --- a/docs/system/riscv/virt.rst +++ b/docs/system/riscv/virt.rst @@ -23,9 +23,9 @@ The ``virt`` machine supports the following devices: * 1 generic PCIe host bridge * The fw_cfg device that allows a guest to obtain data from QEMU -Note that the default CPU is a generic RV32GC/RV64GC. Optional extensions -can be enabled via command line parameters, e.g.: ``-cpu rv64,x-h=true`` -enables the hypervisor extension for RV64. +The hypervisor extension has been enabled for the default CPU, so virtual +machines with hypervisor extension can simply be used without explicitly +declaring. Hardware configuration information ----------------------------------