diff options
29 files changed, 572 insertions, 402 deletions
diff --git a/.gitlab-ci.d/buildtest-template.yml b/.gitlab-ci.d/buildtest-template.yml index 4cc1923931..39da7698b0 100644 --- a/.gitlab-ci.d/buildtest-template.yml +++ b/.gitlab-ci.d/buildtest-template.yml @@ -63,7 +63,6 @@ stage: test image: $CI_REGISTRY_IMAGE/qemu/$IMAGE:$QEMU_CI_CONTAINER_TAG script: - - export RUST_BACKTRACE=1 - source scripts/ci/gitlab-ci-section - section_start buildenv "Setting up to run tests" - scripts/git-submodule.sh update roms/SLOF diff --git a/MAINTAINERS b/MAINTAINERS index 31b395fdfa..8f470a1c9b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -786,7 +786,7 @@ F: docs/system/arm/kzm.rst Integrator CP M: Peter Maydell <peter.maydell@linaro.org> L: qemu-arm@nongnu.org -S: Maintained +S: Odd Fixes F: hw/arm/integratorcp.c F: hw/misc/arm_integrator_debug.c F: include/hw/misc/arm_integrator_debug.h @@ -867,7 +867,7 @@ F: docs/system/arm/mps2.rst Musca M: Peter Maydell <peter.maydell@linaro.org> L: qemu-arm@nongnu.org -S: Maintained +S: Odd Fixes F: hw/arm/musca.c F: docs/system/arm/musca.rst @@ -915,7 +915,7 @@ F: tests/functional/test_aarch64_raspi4.py Real View M: Peter Maydell <peter.maydell@linaro.org> L: qemu-arm@nongnu.org -S: Maintained +S: Odd Fixes F: hw/arm/realview* F: hw/cpu/realview_mpcore.c F: hw/intc/realview_gic.c @@ -965,7 +965,7 @@ F: tests/functional/test_arm_collie.py Stellaris M: Peter Maydell <peter.maydell@linaro.org> L: qemu-arm@nongnu.org -S: Maintained +S: Odd Fixes F: hw/*/stellaris* F: hw/display/ssd03* F: include/hw/input/gamepad.h @@ -995,7 +995,7 @@ F: docs/system/arm/stm32.rst Versatile Express M: Peter Maydell <peter.maydell@linaro.org> L: qemu-arm@nongnu.org -S: Maintained +S: Odd Fixes F: hw/arm/vexpress.c F: hw/display/sii9022.c F: docs/system/arm/vexpress.rst @@ -1004,7 +1004,7 @@ F: tests/functional/test_arm_vexpress.py Versatile PB M: Peter Maydell <peter.maydell@linaro.org> L: qemu-arm@nongnu.org -S: Maintained +S: Odd Fixes F: hw/*/versatile* F: hw/i2c/arm_sbcon_i2c.c F: include/hw/i2c/arm_sbcon_i2c.h @@ -2003,7 +2003,7 @@ F: include/hw/hyperv/vmbus*.h OMAP M: Peter Maydell <peter.maydell@linaro.org> L: qemu-arm@nongnu.org -S: Maintained +S: Odd Fixes F: hw/*/omap* F: include/hw/arm/omap.h F: docs/system/arm/sx1.rst diff --git a/docs/conf.py b/docs/conf.py index a3f9fa63d9..7b5712e122 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -161,6 +161,13 @@ qapi_allowed_fields = { "see also", } +# Due to a limitation in Sphinx, we need to know which indices to +# generate in advance. Adding a namespace here allows that generation. +qapi_namespaces = { + "QGA", + "QMP", + "QSD", +} # -- Options for HTML output ---------------------------------------------- diff --git a/docs/devel/qapi-domain.rst b/docs/devel/qapi-domain.rst index 1475870ca6..a748529f51 100644 --- a/docs/devel/qapi-domain.rst +++ b/docs/devel/qapi-domain.rst @@ -385,13 +385,13 @@ Type names in references can be surrounded by brackets, like ``[typename]``, to indicate an array of that type. The cross-reference will apply only to the type name between the brackets. For example; ``:qapi:type:`[Qcow2BitmapInfoFlags]``` renders to: -:qapi:type:`[Qcow2BitmapInfoFlags]` +:qapi:type:`[QMP:Qcow2BitmapInfoFlags]` To indicate an optional argument/member in a field list, the type name can be suffixed with ``?``. The cross-reference will be transformed to "type, Optional" with the link applying only to the type name. For example; ``:qapi:type:`BitmapSyncMode?``` renders to: -:qapi:type:`BitmapSyncMode?` +:qapi:type:`QMP:BitmapSyncMode?` Namespaces @@ -400,17 +400,38 @@ Namespaces Mimicking the `Python domain target specification syntax <https://www.sphinx-doc.org/en/master/usage/domains/python.html#target-specification>`_, QAPI allows you to specify the fully qualified path for a data -type. QAPI enforces globally unique names, so it's unlikely you'll need -this specific feature, but it may be extended in the near future to -allow referencing identically named commands and data types from -different utilities; i.e. QEMU Storage Daemon vs QMP. +type. +* A namespace can be explicitly provided; + e.g. ``:qapi:type:`QMP:BitmapSyncMode`` * A module can be explicitly provided; - ``:qapi:type:`block-core.BitmapSyncMode``` will render to: - :qapi:type:`block-core.BitmapSyncMode` + ``:qapi:type:`QMP:block-core.BitmapSyncMode``` will render to: + :qapi:type:`QMP:block-core.BitmapSyncMode` * If you don't want to display the "fully qualified" name, it can be - prefixed with a tilde; ``:qapi:type:`~block-core.BitmapSyncMode``` - will render to: :qapi:type:`~block-core.BitmapSyncMode` + prefixed with a tilde; ``:qapi:type:`~QMP:block-core.BitmapSyncMode``` + will render to: :qapi:type:`~QMP:block-core.BitmapSyncMode` + + +Target resolution +----------------- + +Any cross-reference to a QAPI type, whether using the ```any``` style of +reference or the more explicit ```:qapi:any:`target``` syntax, allows +for the presence or absence of either the namespace or module +information. + +When absent, their value will be inferred from context by the presence +of any ``qapi:namespace`` or ``qapi:module`` directives preceding the +cross-reference. + +If no results are found when using the inferred values, other +namespaces/modules will be searched as a last resort; but any explicitly +provided values must always match in order to succeed. + +This allows for efficient cross-referencing with a minimum of syntax in +the large majority of cases, but additional context or namespace markup +may be required outside of the QAPI reference documents when linking to +items that share a name across multiple documented QAPI schema. Custom link text @@ -423,7 +444,7 @@ using the ``custom text <target>`` syntax. For example, ``:qapi:cmd:`Merge dirty bitmaps <block-dirty-bitmap-merge>``` will render as: :qapi:cmd:`Merge dirty -bitmaps <block-dirty-bitmap-merge>` +bitmaps <QMP:block-dirty-bitmap-merge>` Directives @@ -464,8 +485,11 @@ removed in a future version. QAPI standard options --------------------- -All QAPI directives -- *except* for module -- support these common options. +All QAPI directives -- *except* for namespace and module -- support +these common options. +* ``:namespace: name`` -- This option allows you to override the + namespace association of a given definition. * ``:module: modname`` -- Borrowed from the Python domain, this option allows you to override the module association of a given definition. * ``:since: x.y`` -- Allows the documenting of "Since" information, which is @@ -480,6 +504,28 @@ All QAPI directives -- *except* for module -- support these common options. production code. +qapi:namespace +-------------- + +The ``qapi:namespace`` directive marks the start of a QAPI namespace. It +does not take a content body, nor any options. All subsequent QAPI +directives are associated with the most recent namespace. This affects +the definition's "fully qualified name", allowing two different +namespaces to create an otherwise identically named definition. + +This directive also influences how reference resolution works for any +references that do not explicity specify a namespace, so this directive +can be used to nudge references into preferring targets from within that +namespace. + +Example:: + + .. qapi:namespace:: QMP + + +This directive has no visible effect. + + qapi:module ----------- diff --git a/docs/interop/qemu-ga-ref.rst b/docs/interop/qemu-ga-ref.rst index 032d492455..19b5c7a549 100644 --- a/docs/interop/qemu-ga-ref.rst +++ b/docs/interop/qemu-ga-ref.rst @@ -5,3 +5,5 @@ QEMU Guest Agent Protocol Reference :depth: 3 .. qapi-doc:: qga/qapi-schema.json + :transmogrify: + :namespace: QGA diff --git a/docs/interop/qemu-qmp-ref.rst b/docs/interop/qemu-qmp-ref.rst index e95eeac45e..ef8792b53f 100644 --- a/docs/interop/qemu-qmp-ref.rst +++ b/docs/interop/qemu-qmp-ref.rst @@ -8,3 +8,4 @@ QEMU QMP Reference Manual .. qapi-doc:: qapi/qapi-schema.json :transmogrify: + :namespace: QMP diff --git a/docs/interop/qemu-storage-daemon-qmp-ref.rst b/docs/interop/qemu-storage-daemon-qmp-ref.rst index 9fed68152f..d0228d63b8 100644 --- a/docs/interop/qemu-storage-daemon-qmp-ref.rst +++ b/docs/interop/qemu-storage-daemon-qmp-ref.rst @@ -5,3 +5,5 @@ QEMU Storage Daemon QMP Reference Manual :depth: 3 .. qapi-doc:: storage-daemon/qapi/qapi-schema.json + :transmogrify: + :namespace: QSD diff --git a/docs/sphinx/qapi_domain.py b/docs/sphinx/qapi_domain.py index 7ff618d8cd..c94af5719c 100644 --- a/docs/sphinx/qapi_domain.py +++ b/docs/sphinx/qapi_domain.py @@ -7,17 +7,14 @@ QAPI domain extension. from __future__ import annotations +import re +import types from typing import ( TYPE_CHECKING, - AbstractSet, - Any, - Dict, - Iterable, List, NamedTuple, - Optional, Tuple, - Union, + Type, cast, ) @@ -34,7 +31,6 @@ from compat import ( SpaceNode, ) from sphinx import addnodes -from sphinx.addnodes import desc_signature, pending_xref from sphinx.directives import ObjectDescription from sphinx.domains import ( Domain, @@ -45,17 +41,29 @@ from sphinx.domains import ( from sphinx.locale import _, __ from sphinx.roles import XRefRole from sphinx.util import logging +from sphinx.util.docutils import SphinxDirective from sphinx.util.nodes import make_id, make_refnode if TYPE_CHECKING: + from typing import ( + AbstractSet, + Any, + Dict, + Iterable, + Optional, + Union, + ) + from docutils.nodes import Element, Node + from sphinx.addnodes import desc_signature, pending_xref from sphinx.application import Sphinx from sphinx.builders import Builder from sphinx.environment import BuildEnvironment from sphinx.util.typing import OptionSpec + logger = logging.getLogger(__name__) @@ -89,6 +97,7 @@ class QAPIXRefRole(XRefRole): title: str, target: str, ) -> tuple[str, str]: + refnode["qapi:namespace"] = env.ref_context.get("qapi:namespace") refnode["qapi:module"] = env.ref_context.get("qapi:module") # Cross-references that begin with a tilde adjust the title to @@ -174,6 +183,30 @@ class QAPIDescription(ParserFix): # NB: this is used for the global index, not the QAPI index. return ("single", f"{name} (QMP {self.objtype})") + def _get_context(self) -> Tuple[str, str]: + namespace = self.options.get( + "namespace", self.env.ref_context.get("qapi:namespace", "") + ) + modname = self.options.get( + "module", self.env.ref_context.get("qapi:module", "") + ) + + return namespace, modname + + def _get_fqn(self, name: Signature) -> str: + namespace, modname = self._get_context() + + # If we're documenting a module, don't include the module as + # part of the FQN; we ARE the module! + if self.objtype == "module": + modname = "" + + if modname: + name = f"{modname}.{name}" + if namespace: + name = f"{namespace}:{name}" + return name + def add_target_and_index( self, name: Signature, sig: str, signode: desc_signature ) -> None: @@ -183,14 +216,8 @@ class QAPIDescription(ParserFix): assert self.objtype - # If we're documenting a module, don't include the module as - # part of the FQN. - modname = "" - if self.objtype != "module": - modname = self.options.get( - "module", self.env.ref_context.get("qapi:module") - ) - fullname = (modname + "." if modname else "") + name + if not (fullname := signode.get("fullname", "")): + fullname = self._get_fqn(name) node_id = make_id( self.env, self.state.document, self.objtype, fullname @@ -209,18 +236,26 @@ class QAPIDescription(ParserFix): (arity, indextext, node_id, "", None) ) + @staticmethod + def split_fqn(name: str) -> Tuple[str, str, str]: + if ":" in name: + ns, name = name.split(":") + else: + ns = "" + + if "." in name: + module, name = name.split(".") + else: + module = "" + + return (ns, module, name) + def _object_hierarchy_parts( self, sig_node: desc_signature ) -> Tuple[str, ...]: if "fullname" not in sig_node: return () - modname = sig_node.get("module") - fullname = sig_node["fullname"] - - if modname: - return (modname, *fullname.split(".")) - - return tuple(fullname.split(".")) + return self.split_fqn(sig_node["fullname"]) def _toc_entry_name(self, sig_node: desc_signature) -> str: # This controls the name in the TOC and on the sidebar. @@ -231,13 +266,23 @@ class QAPIDescription(ParserFix): return "" config = self.env.app.config - *parents, name = toc_parts + namespace, modname, name = toc_parts + if config.toc_object_entries_show_parents == "domain": - return sig_node.get("fullname", name) + ret = name + if modname and modname != self.env.ref_context.get( + "qapi:module", "" + ): + ret = f"{modname}.{name}" + if namespace and namespace != self.env.ref_context.get( + "qapi:namespace", "" + ): + ret = f"{namespace}:{ret}" + return ret if config.toc_object_entries_show_parents == "hide": return name if config.toc_object_entries_show_parents == "all": - return ".".join(parents + [name]) + return sig_node.get("fullname", name) return "" @@ -254,8 +299,9 @@ class QAPIObject(QAPIDescription): ) option_spec.update( { - # Borrowed from the Python domain: - "module": directives.unchanged, # Override contextual module name + # Context overrides: + "namespace": directives.unchanged, + "module": directives.unchanged, # These are QAPI originals: "since": directives.unchanged, "ifcond": directives.unchanged, @@ -308,12 +354,15 @@ class QAPIObject(QAPIDescription): As such, the only argument here is "sig", which is just the QAPI definition name. """ - modname = self.options.get( - "module", self.env.ref_context.get("qapi:module") - ) + # No module or domain info allowed in the signature! + assert ":" not in sig + assert "." not in sig - signode["fullname"] = sig + namespace, modname = self._get_context() + signode["fullname"] = self._get_fqn(sig) + signode["namespace"] = namespace signode["module"] = modname + sig_prefix = self.get_signature_prefix() if sig_prefix: signode += addnodes.desc_annotation( @@ -601,6 +650,17 @@ class QAPIModule(QAPIDescription): return ret +class QAPINamespace(SphinxDirective): + has_content = False + required_arguments = 1 + + def run(self) -> List[Node]: + namespace = self.arguments[0].strip() + self.env.ref_context["qapi:namespace"] = namespace + + return [] + + class QAPIIndex(Index): """ Index subclass to provide the QAPI definition index. @@ -611,6 +671,7 @@ class QAPIIndex(Index): name = "index" localname = _("QAPI Index") shortname = _("QAPI Index") + namespace = "" def generate( self, @@ -620,25 +681,20 @@ class QAPIIndex(Index): content: Dict[str, List[IndexEntry]] = {} collapse = False - # list of all object (name, ObjectEntry) pairs, sorted by name - # (ignoring the module) - objects = sorted( - self.domain.objects.items(), - key=lambda x: x[0].split(".")[-1].lower(), - ) - - for objname, obj in objects: + for objname, obj in self.domain.objects.items(): if docnames and obj.docname not in docnames: continue - # Strip the module name out: - objname = objname.split(".")[-1] + ns, _mod, name = QAPIDescription.split_fqn(objname) + + if self.namespace != ns: + continue # Add an alphabetical entry: - entries = content.setdefault(objname[0].upper(), []) + entries = content.setdefault(name[0].upper(), []) entries.append( IndexEntry( - objname, 0, obj.docname, obj.node_id, obj.objtype, "", "" + name, 0, obj.docname, obj.node_id, obj.objtype, "", "" ) ) @@ -646,10 +702,14 @@ class QAPIIndex(Index): category = obj.objtype.title() + "s" entries = content.setdefault(category, []) entries.append( - IndexEntry(objname, 0, obj.docname, obj.node_id, "", "", "") + IndexEntry(name, 0, obj.docname, obj.node_id, "", "", "") ) - # alphabetically sort categories; type names first, ABC entries last. + # Sort entries within each category alphabetically + for category in content: + content[category] = sorted(content[category]) + + # Sort the categories themselves; type names first, ABC entries last. sorted_content = sorted( content.items(), key=lambda x: (len(x[0]) == 1, x[0]), @@ -682,6 +742,7 @@ class QAPIDomain(Domain): # Each of these provides a rST directive, # e.g. .. qapi:module:: block-core directives = { + "namespace": QAPINamespace, "module": QAPIModule, "command": QAPICommand, "event": QAPIEvent, @@ -721,6 +782,21 @@ class QAPIDomain(Domain): ret = self.data.setdefault("objects", {}) return ret # type: ignore[no-any-return] + def setup(self) -> None: + namespaces = set(self.env.app.config.qapi_namespaces) + for namespace in namespaces: + new_index: Type[QAPIIndex] = types.new_class( + f"{namespace}Index", bases=(QAPIIndex,) + ) + new_index.name = f"{namespace.lower()}-index" + new_index.localname = _(f"{namespace} Index") + new_index.shortname = _(f"{namespace} Index") + new_index.namespace = namespace + + self.indices.append(new_index) + + super().setup() + def note_object( self, name: str, @@ -773,40 +849,44 @@ class QAPIDomain(Domain): self.objects[fullname] = obj def find_obj( - self, modname: str, name: str, typ: Optional[str] - ) -> list[tuple[str, ObjectEntry]]: + self, namespace: str, modname: str, name: str, typ: Optional[str] + ) -> List[Tuple[str, ObjectEntry]]: """ - Find a QAPI object for "name", perhaps using the given module. + Find a QAPI object for "name", maybe using contextual information. Returns a list of (name, object entry) tuples. - :param modname: The current module context (if any!) - under which we are searching. - :param name: The name of the x-ref to resolve; - may or may not include a leading module. - :param type: The role name of the x-ref we're resolving, if provided. - (This is absent for "any" lookups.) + :param namespace: The current namespace context (if any!) under + which we are searching. + :param modname: The current module context (if any!) under + which we are searching. + :param name: The name of the x-ref to resolve; may or may not + include leading context. + :param type: The role name of the x-ref we're resolving, if + provided. This is absent for "any" role lookups. """ if not name: return [] - names: list[str] = [] - matches: list[tuple[str, ObjectEntry]] = [] + # ## + # what to search for + # ## - fullname = name - if "." in fullname: - # We're searching for a fully qualified reference; - # ignore the contextual module. - pass - elif modname: - # We're searching for something from somewhere; - # try searching the current module first. - # e.g. :qapi:cmd:`query-block` or `query-block` is being searched. - fullname = f"{modname}.{name}" + parts = list(QAPIDescription.split_fqn(name)) + explicit = tuple(bool(x) for x in parts) + + # Fill in the blanks where possible: + if namespace and not parts[0]: + parts[0] = namespace + if modname and not parts[1]: + parts[1] = modname + + implicit_fqn = "" + if all(parts): + implicit_fqn = f"{parts[0]}:{parts[1]}.{parts[2]}" if typ is None: - # type isn't specified, this is a generic xref. - # search *all* qapi-specific object types. + # :any: lookup, search everything: objtypes: List[str] = list(self.object_types) else: # type is specified and will be a role (e.g. obj, mod, cmd) @@ -814,25 +894,57 @@ class QAPIDomain(Domain): # using the QAPIDomain.object_types table. objtypes = self.objtypes_for_role(typ, []) - if name in self.objects and self.objects[name].objtype in objtypes: - names = [name] - elif ( - fullname in self.objects - and self.objects[fullname].objtype in objtypes - ): - names = [fullname] - else: - # exact match wasn't found; e.g. we are searching for - # `query-block` from a different (or no) module. - searchname = "." + name - names = [ - oname - for oname in self.objects - if oname.endswith(searchname) - and self.objects[oname].objtype in objtypes - ] + # ## + # search! + # ## - matches = [(oname, self.objects[oname]) for oname in names] + def _search(needle: str) -> List[str]: + if ( + needle + and needle in self.objects + and self.objects[needle].objtype in objtypes + ): + return [needle] + return [] + + if found := _search(name): + # Exact match! + pass + elif found := _search(implicit_fqn): + # Exact match using contextual information to fill in the gaps. + pass + else: + # No exact hits, perform applicable fuzzy searches. + searches = [] + + esc = tuple(re.escape(s) for s in parts) + + # Try searching for ns:*.name or ns:name + if explicit[0] and not explicit[1]: + searches.append(f"^{esc[0]}:([^\\.]+\\.)?{esc[2]}$") + # Try searching for *:module.name or module.name + if explicit[1] and not explicit[0]: + searches.append(f"(^|:){esc[1]}\\.{esc[2]}$") + # Try searching for context-ns:*.name or context-ns:name + if parts[0] and not (explicit[0] or explicit[1]): + searches.append(f"^{esc[0]}:([^\\.]+\\.)?{esc[2]}$") + # Try searching for *:context-mod.name or context-mod.name + if parts[1] and not (explicit[0] or explicit[1]): + searches.append(f"(^|:){esc[1]}\\.{esc[2]}$") + # Try searching for *:name, *.name, or name + if not (explicit[0] or explicit[1]): + searches.append(f"(^|:|\\.){esc[2]}$") + + for search in searches: + if found := [ + oname + for oname in self.objects + if re.search(search, oname) + and self.objects[oname].objtype in objtypes + ]: + break + + matches = [(oname, self.objects[oname]) for oname in found] if len(matches) > 1: matches = [m for m in matches if not m[1].aliased] return matches @@ -847,8 +959,9 @@ class QAPIDomain(Domain): node: pending_xref, contnode: Element, ) -> nodes.reference | None: + namespace = node.get("qapi:namespace") modname = node.get("qapi:module") - matches = self.find_obj(modname, target, typ) + matches = self.find_obj(namespace, modname, target, typ) if not matches: # Normally, we could pass warn_dangling=True to QAPIXRefRole(), @@ -901,7 +1014,9 @@ class QAPIDomain(Domain): contnode: Element, ) -> List[Tuple[str, nodes.reference]]: results: List[Tuple[str, nodes.reference]] = [] - matches = self.find_obj(node.get("qapi:module"), target, None) + matches = self.find_obj( + node.get("qapi:namespace"), node.get("qapi:module"), target, None + ) for name, obj in matches: rolename = self.role_for_objtype(obj.objtype) assert rolename is not None @@ -921,6 +1036,12 @@ def setup(app: Sphinx) -> Dict[str, Any]: "env", # Setting impacts parsing phase types=set, ) + app.add_config_value( + "qapi_namespaces", + set(), + "env", + types=set, + ) app.add_domain(QAPIDomain) return { diff --git a/docs/sphinx/qapidoc.py b/docs/sphinx/qapidoc.py index 432fef04b0..661b2c4ed0 100644 --- a/docs/sphinx/qapidoc.py +++ b/docs/sphinx/qapidoc.py @@ -451,6 +451,12 @@ class Transmogrifier: finally: self._curr_ent = None + def set_namespace(self, namespace: str, source: str, lineno: int) -> None: + self.add_line_raw( + f".. qapi:namespace:: {namespace}", source, lineno + 1 + ) + self.ensure_blank_line() + class QAPISchemaGenDepVisitor(QAPISchemaVisitor): """A QAPI schema visitor which adds Sphinx dependencies each module @@ -496,6 +502,7 @@ class QAPIDocDirective(NestedDirective): optional_arguments = 1 option_spec = { "qapifile": directives.unchanged_required, + "namespace": directives.unchanged, "transmogrify": directives.flag, } has_content = False @@ -510,6 +517,11 @@ class QAPIDocDirective(NestedDirective): vis = Transmogrifier() modules = set() + if "namespace" in self.options: + vis.set_namespace( + self.options["namespace"], *self.get_source_info() + ) + for doc in schema.docs: module_source = doc.info.fname if module_source not in modules: diff --git a/hw/char/pl011.c b/hw/char/pl011.c index 23a9db8c57..0e9ec1301d 100644 --- a/hw/char/pl011.c +++ b/hw/char/pl011.c @@ -490,16 +490,17 @@ static int pl011_can_receive(void *opaque) unsigned fifo_depth = pl011_get_fifo_depth(s); unsigned fifo_available = fifo_depth - s->read_count; - if (!(s->cr & CR_UARTEN)) { - qemu_log_mask(LOG_GUEST_ERROR, - "PL011 receiving data on disabled UART\n"); - } - if (!(s->cr & CR_RXE)) { - qemu_log_mask(LOG_GUEST_ERROR, - "PL011 receiving data on disabled RX UART\n"); - } - trace_pl011_can_receive(s->lcr, s->read_count, fifo_depth, fifo_available); + /* + * In theory we should check the UART and RX enable bits here and + * return 0 if they are not set (so the guest can't receive data + * until you have enabled the UART). In practice we suspect there + * is at least some guest code out there which has been tested only + * on QEMU and which never bothers to enable the UART because we + * historically never enforced that. So we effectively keep the + * UART continuously enabled regardless of the enable bits. + */ + trace_pl011_can_receive(s->lcr, s->read_count, fifo_depth, fifo_available); return fifo_available; } diff --git a/hw/intc/arm_gicv3_cpuif.c b/hw/intc/arm_gicv3_cpuif.c index 7f1d071c19..de37465bc8 100644 --- a/hw/intc/arm_gicv3_cpuif.c +++ b/hw/intc/arm_gicv3_cpuif.c @@ -22,6 +22,7 @@ #include "cpu.h" #include "target/arm/cpregs.h" #include "target/arm/cpu-features.h" +#include "target/arm/internals.h" #include "system/tcg.h" #include "system/qtest.h" diff --git a/linux-user/aarch64/cpu_loop.c b/linux-user/aarch64/cpu_loop.c index c5d8a483a3..fea43cefa6 100644 --- a/linux-user/aarch64/cpu_loop.c +++ b/linux-user/aarch64/cpu_loop.c @@ -27,54 +27,6 @@ #include "target/arm/syndrome.h" #include "target/arm/cpu-features.h" -#define get_user_code_u32(x, gaddr, env) \ - ({ abi_long __r = get_user_u32((x), (gaddr)); \ - if (!__r && bswap_code(arm_sctlr_b(env))) { \ - (x) = bswap32(x); \ - } \ - __r; \ - }) - -#define get_user_code_u16(x, gaddr, env) \ - ({ abi_long __r = get_user_u16((x), (gaddr)); \ - if (!__r && bswap_code(arm_sctlr_b(env))) { \ - (x) = bswap16(x); \ - } \ - __r; \ - }) - -#define get_user_data_u32(x, gaddr, env) \ - ({ abi_long __r = get_user_u32((x), (gaddr)); \ - if (!__r && arm_cpu_bswap_data(env)) { \ - (x) = bswap32(x); \ - } \ - __r; \ - }) - -#define get_user_data_u16(x, gaddr, env) \ - ({ abi_long __r = get_user_u16((x), (gaddr)); \ - if (!__r && arm_cpu_bswap_data(env)) { \ - (x) = bswap16(x); \ - } \ - __r; \ - }) - -#define put_user_data_u32(x, gaddr, env) \ - ({ typeof(x) __x = (x); \ - if (arm_cpu_bswap_data(env)) { \ - __x = bswap32(__x); \ - } \ - put_user_u32(__x, (gaddr)); \ - }) - -#define put_user_data_u16(x, gaddr, env) \ - ({ typeof(x) __x = (x); \ - if (arm_cpu_bswap_data(env)) { \ - __x = bswap16(__x); \ - } \ - put_user_u16(__x, (gaddr)); \ - }) - /* AArch64 main loop */ void cpu_loop(CPUARMState *env) { diff --git a/linux-user/arm/cpu_loop.c b/linux-user/arm/cpu_loop.c index 10d8561f9b..7416e3216e 100644 --- a/linux-user/arm/cpu_loop.c +++ b/linux-user/arm/cpu_loop.c @@ -36,45 +36,10 @@ __r; \ }) -#define get_user_code_u16(x, gaddr, env) \ - ({ abi_long __r = get_user_u16((x), (gaddr)); \ - if (!__r && bswap_code(arm_sctlr_b(env))) { \ - (x) = bswap16(x); \ - } \ - __r; \ - }) - -#define get_user_data_u32(x, gaddr, env) \ - ({ abi_long __r = get_user_u32((x), (gaddr)); \ - if (!__r && arm_cpu_bswap_data(env)) { \ - (x) = bswap32(x); \ - } \ - __r; \ - }) - -#define get_user_data_u16(x, gaddr, env) \ - ({ abi_long __r = get_user_u16((x), (gaddr)); \ - if (!__r && arm_cpu_bswap_data(env)) { \ - (x) = bswap16(x); \ - } \ - __r; \ - }) - -#define put_user_data_u32(x, gaddr, env) \ - ({ typeof(x) __x = (x); \ - if (arm_cpu_bswap_data(env)) { \ - __x = bswap32(__x); \ - } \ - put_user_u32(__x, (gaddr)); \ - }) - -#define put_user_data_u16(x, gaddr, env) \ - ({ typeof(x) __x = (x); \ - if (arm_cpu_bswap_data(env)) { \ - __x = bswap16(__x); \ - } \ - put_user_u16(__x, (gaddr)); \ - }) +/* + * Note that if we need to do data accesses here, they should do a + * bswap if arm_cpu_bswap_data() returns true. + */ /* * Similar to code in accel/tcg/user-exec.c, but outside the execution loop. diff --git a/meson.build b/meson.build index 2f43fd81bf..7f75256acf 100644 --- a/meson.build +++ b/meson.build @@ -5,9 +5,12 @@ project('qemu', ['c'], meson_version: '>=1.5.0', meson.add_devenv({ 'MESON_BUILD_ROOT' : meson.project_build_root() }) -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: ['slow', 'thorough'], is_default: true, + env: ['RUST_BACKTRACE=1']) +add_test_setup('slow', exclude_suites: ['thorough'], + env: ['G_TEST_SLOW=1', 'SPEED=slow', 'RUST_BACKTRACE=1']) +add_test_setup('thorough', + env: ['G_TEST_SLOW=1', 'SPEED=thorough', 'RUST_BACKTRACE=1']) meson.add_postconf_script(find_program('scripts/symlink-install-tree.py')) diff --git a/qapi/block-core.json b/qapi/block-core.json index ee6eccc68c..b1937780e1 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -5913,35 +5913,31 @@ ## # @x-blockdev-change: # -# Dynamically reconfigure the block driver state graph. It can be -# used to add, remove, insert or replace a graph node. Currently only -# the Quorum driver implements this feature to add or remove its -# child. This is useful to fix a broken quorum child. +# Dynamically reconfigure the block driver state graph. # -# If @node is specified, it will be inserted under @parent. @child -# may not be specified in this case. If both @parent and @child are -# specified but @node is not, @child will be detached from @parent. +# Currently only supports adding and deleting quorum children. A +# child will be added at the end of the list of children. Its +# contents *must* be consistent with the other childrens' contents. +# Deleting a child that is not last in the list of children is +# problematic, because it "renumbers" the children following it. # # @parent: the id or name of the parent node. # -# @child: the name of a child under the given parent node. +# @child: the name of a child to be deleted. Mutually exclusive with +# @node. # -# @node: the name of the node that will be added. +# @node: the name of the node to be added. Mutually exclusive with +# @child. # # Features: # -# @unstable: This command is experimental, and its API is not stable. -# It does not support all kinds of operations, all kinds of -# children, nor all block drivers. +# @unstable: This command is experimental. # -# FIXME Removing children from a quorum node means introducing +# TODO: Removing children from a quorum node means introducing # gaps in the child indices. This cannot be represented in the # 'children' list of BlockdevOptionsQuorum, as returned by # .bdrv_refresh_filename(). # -# Warning: The data in a new quorum child MUST be consistent with -# that of the rest of the array. -# # Since: 2.7 # # .. qmp-example:: diff --git a/qapi/qapi-schema.json b/qapi/qapi-schema.json index 4475e81cc3..c41c01eb2a 100644 --- a/qapi/qapi-schema.json +++ b/qapi/qapi-schema.json @@ -5,7 +5,7 @@ # # This document describes all commands currently supported by QMP. # -# For locating a particular item, please see the `qapi-index`. +# For locating a particular item, please see the `qapi-qmp-index`. # # Most of the time their usage is exactly the same as in the user # Monitor, this means that any other document which also describe diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json index 995594aaf4..35ec0e7db3 100644 --- a/qga/qapi-schema.json +++ b/qga/qapi-schema.json @@ -3,6 +3,9 @@ ## # = QEMU guest agent protocol commands and structs +# +# For a concise listing of all commands, events, and types in the QEMU +# guest agent, please consult the `qapi-qga-index`. ## { 'pragma': { 'doc-required': true } } diff --git a/storage-daemon/qapi/qapi-schema.json b/storage-daemon/qapi/qapi-schema.json index f10c949490..2a562ee32e 100644 --- a/storage-daemon/qapi/qapi-schema.json +++ b/storage-daemon/qapi/qapi-schema.json @@ -13,6 +13,14 @@ # the array type in the main schema, even if it is unused outside of the # storage daemon. +## +# = QEMU storage daemon protocol commands and structs +# +# For a concise listing of all commands, events, and types in the QEMU +# storage daemon, please consult the `qapi-qsd-index`. +## + + { 'include': '../../qapi/pragma.json' } # Documentation generated with qapi-gen.py is in source order, with diff --git a/target/arm/arch_dump.c b/target/arm/arch_dump.c index 5c943dc27b..c40df4e7fd 100644 --- a/target/arm/arch_dump.c +++ b/target/arm/arch_dump.c @@ -23,6 +23,7 @@ #include "elf.h" #include "system/dump.h" #include "cpu-features.h" +#include "internals.h" /* struct user_pt_regs from arch/arm64/include/uapi/asm/ptrace.h */ struct aarch64_user_regs { diff --git a/target/arm/cpregs.h b/target/arm/cpregs.h index 52377c6eb5..2183de8eda 100644 --- a/target/arm/cpregs.h +++ b/target/arm/cpregs.h @@ -1157,4 +1157,32 @@ static inline bool arm_cpreg_traps_in_nv(const ARMCPRegInfo *ri) return ri->opc1 == 4 || ri->opc1 == 5; } +/* Macros for accessing a specified CP register bank */ +#define A32_BANKED_REG_GET(_env, _regname, _secure) \ + ((_secure) ? (_env)->cp15._regname##_s : (_env)->cp15._regname##_ns) + +#define A32_BANKED_REG_SET(_env, _regname, _secure, _val) \ + do { \ + if (_secure) { \ + (_env)->cp15._regname##_s = (_val); \ + } else { \ + (_env)->cp15._regname##_ns = (_val); \ + } \ + } while (0) + +/* + * Macros for automatically accessing a specific CP register bank depending on + * the current secure state of the system. These macros are not intended for + * supporting instruction translation reads/writes as these are dependent + * solely on the SCR.NS bit and not the mode. + */ +#define A32_BANKED_CURRENT_REG_GET(_env, _regname) \ + A32_BANKED_REG_GET((_env), _regname, \ + (arm_is_secure(_env) && !arm_el_is_aa64((_env), 3))) + +#define A32_BANKED_CURRENT_REG_SET(_env, _regname, _val) \ + A32_BANKED_REG_SET((_env), _regname, \ + (arm_is_secure(_env) && !arm_el_is_aa64((_env), 3)), \ + (_val)) + #endif /* TARGET_ARM_CPREGS_H */ diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 8f52380c88..a8177c6c2e 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -2635,81 +2635,15 @@ uint64_t arm_hcr_el2_eff_secstate(CPUARMState *env, ARMSecuritySpace space); uint64_t arm_hcr_el2_eff(CPUARMState *env); uint64_t arm_hcrx_el2_eff(CPUARMState *env); -/* Return true if the specified exception level is running in AArch64 state. */ -static inline bool arm_el_is_aa64(CPUARMState *env, int el) -{ - /* This isn't valid for EL0 (if we're in EL0, is_a64() is what you want, - * and if we're not in EL0 then the state of EL0 isn't well defined.) - */ - assert(el >= 1 && el <= 3); - bool aa64 = arm_feature(env, ARM_FEATURE_AARCH64); - - /* The highest exception level is always at the maximum supported - * register width, and then lower levels have a register width controlled - * by bits in the SCR or HCR registers. - */ - if (el == 3) { - return aa64; - } - - if (arm_feature(env, ARM_FEATURE_EL3) && - ((env->cp15.scr_el3 & SCR_NS) || !(env->cp15.scr_el3 & SCR_EEL2))) { - aa64 = aa64 && (env->cp15.scr_el3 & SCR_RW); - } - - if (el == 2) { - return aa64; - } - - if (arm_is_el2_enabled(env)) { - aa64 = aa64 && (env->cp15.hcr_el2 & HCR_RW); - } - - return aa64; -} - -/* Function for determining whether guest cp register reads and writes should +/* + * Function for determining whether guest cp register reads and writes should * access the secure or non-secure bank of a cp register. When EL3 is * operating in AArch32 state, the NS-bit determines whether the secure * instance of a cp register should be used. When EL3 is AArch64 (or if * it doesn't exist at all) then there is no register banking, and all * accesses are to the non-secure version. */ -static inline bool access_secure_reg(CPUARMState *env) -{ - bool ret = (arm_feature(env, ARM_FEATURE_EL3) && - !arm_el_is_aa64(env, 3) && - !(env->cp15.scr_el3 & SCR_NS)); - - return ret; -} - -/* Macros for accessing a specified CP register bank */ -#define A32_BANKED_REG_GET(_env, _regname, _secure) \ - ((_secure) ? (_env)->cp15._regname##_s : (_env)->cp15._regname##_ns) - -#define A32_BANKED_REG_SET(_env, _regname, _secure, _val) \ - do { \ - if (_secure) { \ - (_env)->cp15._regname##_s = (_val); \ - } else { \ - (_env)->cp15._regname##_ns = (_val); \ - } \ - } while (0) - -/* Macros for automatically accessing a specific CP register bank depending on - * the current secure state of the system. These macros are not intended for - * supporting instruction translation reads/writes as these are dependent - * solely on the SCR.NS bit and not the mode. - */ -#define A32_BANKED_CURRENT_REG_GET(_env, _regname) \ - A32_BANKED_REG_GET((_env), _regname, \ - (arm_is_secure(_env) && !arm_el_is_aa64((_env), 3))) - -#define A32_BANKED_CURRENT_REG_SET(_env, _regname, _val) \ - A32_BANKED_REG_SET((_env), _regname, \ - (arm_is_secure(_env) && !arm_el_is_aa64((_env), 3)), \ - (_val)) +bool access_secure_reg(CPUARMState *env); uint32_t arm_phys_excp_target_el(CPUState *cs, uint32_t excp_idx, uint32_t cur_el, bool secure); @@ -2732,39 +2666,6 @@ static inline bool arm_v7m_is_handler_mode(CPUARMState *env) return env->v7m.exception != 0; } -/* Return the current Exception Level (as per ARMv8; note that this differs - * from the ARMv7 Privilege Level). - */ -static inline int arm_current_el(CPUARMState *env) -{ - if (arm_feature(env, ARM_FEATURE_M)) { - return arm_v7m_is_handler_mode(env) || - !(env->v7m.control[env->v7m.secure] & 1); - } - - if (is_a64(env)) { - return extract32(env->pstate, 2, 2); - } - - switch (env->uncached_cpsr & 0x1f) { - case ARM_CPU_MODE_USR: - return 0; - case ARM_CPU_MODE_HYP: - return 2; - case ARM_CPU_MODE_MON: - return 3; - default: - if (arm_is_secure(env) && !arm_el_is_aa64(env, 3)) { - /* If EL3 is 32-bit then all secure privileged modes run in - * EL3 - */ - return 3; - } - - return 1; - } -} - /** * write_list_to_cpustate * @cpu: ARMCPU @@ -3065,47 +2966,6 @@ static inline bool arm_sctlr_b(CPUARMState *env) uint64_t arm_sctlr(CPUARMState *env, int el); -static inline bool arm_cpu_data_is_big_endian_a32(CPUARMState *env, - bool sctlr_b) -{ -#ifdef CONFIG_USER_ONLY - /* - * In system mode, BE32 is modelled in line with the - * architecture (as word-invariant big-endianness), where loads - * and stores are done little endian but from addresses which - * are adjusted by XORing with the appropriate constant. So the - * endianness to use for the raw data access is not affected by - * SCTLR.B. - * In user mode, however, we model BE32 as byte-invariant - * big-endianness (because user-only code cannot tell the - * difference), and so we need to use a data access endianness - * that depends on SCTLR.B. - */ - if (sctlr_b) { - return true; - } -#endif - /* In 32bit endianness is determined by looking at CPSR's E bit */ - return env->uncached_cpsr & CPSR_E; -} - -static inline bool arm_cpu_data_is_big_endian_a64(int el, uint64_t sctlr) -{ - return sctlr & (el ? SCTLR_EE : SCTLR_E0E); -} - -/* Return true if the processor is in big-endian mode. */ -static inline bool arm_cpu_data_is_big_endian(CPUARMState *env) -{ - if (!is_a64(env)) { - return arm_cpu_data_is_big_endian_a32(env, arm_sctlr_b(env)); - } else { - int cur_el = arm_current_el(env); - uint64_t sctlr = arm_sctlr(env, cur_el); - return arm_cpu_data_is_big_endian_a64(cur_el, sctlr); - } -} - #include "exec/cpu-all.h" /* @@ -3291,13 +3151,6 @@ static inline bool bswap_code(bool sctlr_b) #endif } -#ifdef CONFIG_USER_ONLY -static inline bool arm_cpu_bswap_data(CPUARMState *env) -{ - return TARGET_BIG_ENDIAN ^ arm_cpu_data_is_big_endian(env); -} -#endif - void cpu_get_tb_cpu_state(CPUARMState *env, vaddr *pc, uint64_t *cs_base, uint32_t *flags); diff --git a/target/arm/helper.c b/target/arm/helper.c index f0ead22937..bb445e30cd 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -5326,6 +5326,11 @@ static void do_hcr_write(CPUARMState *env, uint64_t value, uint64_t valid_mask) /* Clear RES0 bits. */ value &= valid_mask; + /* RW is RAO/WI if EL1 is AArch64 only */ + if (!cpu_isar_feature(aa64_aa32_el1, cpu)) { + value |= HCR_RW; + } + /* * These bits change the MMU setup: * HCR_VM enables stage 2 translation @@ -5383,6 +5388,12 @@ static void hcr_writelow(CPUARMState *env, const ARMCPRegInfo *ri, do_hcr_write(env, value, MAKE_64BIT_MASK(32, 32)); } +static void hcr_reset(CPUARMState *env, const ARMCPRegInfo *ri) +{ + /* hcr_write will set the RES1 bits on an AArch64-only CPU */ + hcr_write(env, ri, 0); +} + /* * Return the effective value of HCR_EL2, at the given security state. * Bits that are not included here: @@ -5618,6 +5629,7 @@ static const ARMCPRegInfo el2_cp_reginfo[] = { .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 0, .access = PL2_RW, .fieldoffset = offsetof(CPUARMState, cp15.hcr_el2), .nv2_redirect_offset = 0x78, + .resetfn = hcr_reset, .writefn = hcr_write, .raw_writefn = raw_write }, { .name = "HCR", .state = ARM_CP_STATE_AA32, .type = ARM_CP_ALIAS | ARM_CP_IO, @@ -9818,7 +9830,7 @@ uint32_t arm_phys_excp_target_el(CPUState *cs, uint32_t excp_idx, uint64_t hcr_el2; if (arm_feature(env, ARM_FEATURE_EL3)) { - rw = ((env->cp15.scr_el3 & SCR_RW) == SCR_RW); + rw = arm_scr_rw_eff(env); } else { /* * Either EL2 is the highest EL (and so the EL2 register width @@ -10627,7 +10639,7 @@ static void arm_cpu_do_interrupt_aarch64(CPUState *cs) switch (new_el) { case 3: - is_aa64 = (env->cp15.scr_el3 & SCR_RW) != 0; + is_aa64 = arm_scr_rw_eff(env); break; case 2: hcr = arm_hcr_el2_eff(env); diff --git a/target/arm/internals.h b/target/arm/internals.h index bb96238919..28585c0755 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -392,6 +392,141 @@ static inline FloatRoundMode arm_rmode_to_sf(ARMFPRounding rmode) return arm_rmode_to_sf_map[rmode]; } +/* Return the effective value of SCR_EL3.RW */ +static inline bool arm_scr_rw_eff(CPUARMState *env) +{ + /* + * SCR_EL3.RW has an effective value of 1 if: + * - we are NS and EL2 is implemented but doesn't support AArch32 + * - we are S and EL2 is enabled (in which case it must be AArch64) + */ + ARMCPU *cpu = env_archcpu(env); + + if (env->cp15.scr_el3 & SCR_RW) { + return true; + } + if (env->cp15.scr_el3 & SCR_NS) { + return arm_feature(env, ARM_FEATURE_EL2) && + !cpu_isar_feature(aa64_aa32_el2, cpu); + } else { + return env->cp15.scr_el3 & SCR_EEL2; + } +} + +/* Return true if the specified exception level is running in AArch64 state. */ +static inline bool arm_el_is_aa64(CPUARMState *env, int el) +{ + /* + * This isn't valid for EL0 (if we're in EL0, is_a64() is what you want, + * and if we're not in EL0 then the state of EL0 isn't well defined.) + */ + assert(el >= 1 && el <= 3); + bool aa64 = arm_feature(env, ARM_FEATURE_AARCH64); + + /* + * The highest exception level is always at the maximum supported + * register width, and then lower levels have a register width controlled + * by bits in the SCR or HCR registers. + */ + if (el == 3) { + return aa64; + } + + if (arm_feature(env, ARM_FEATURE_EL3)) { + aa64 = aa64 && arm_scr_rw_eff(env); + } + + if (el == 2) { + return aa64; + } + + if (arm_is_el2_enabled(env)) { + aa64 = aa64 && (env->cp15.hcr_el2 & HCR_RW); + } + + return aa64; +} + +/* + * Return the current Exception Level (as per ARMv8; note that this differs + * from the ARMv7 Privilege Level). + */ +static inline int arm_current_el(CPUARMState *env) +{ + if (arm_feature(env, ARM_FEATURE_M)) { + return arm_v7m_is_handler_mode(env) || + !(env->v7m.control[env->v7m.secure] & 1); + } + + if (is_a64(env)) { + return extract32(env->pstate, 2, 2); + } + + switch (env->uncached_cpsr & 0x1f) { + case ARM_CPU_MODE_USR: + return 0; + case ARM_CPU_MODE_HYP: + return 2; + case ARM_CPU_MODE_MON: + return 3; + default: + if (arm_is_secure(env) && !arm_el_is_aa64(env, 3)) { + /* If EL3 is 32-bit then all secure privileged modes run in EL3 */ + return 3; + } + + return 1; + } +} + +static inline bool arm_cpu_data_is_big_endian_a32(CPUARMState *env, + bool sctlr_b) +{ +#ifdef CONFIG_USER_ONLY + /* + * In system mode, BE32 is modelled in line with the + * architecture (as word-invariant big-endianness), where loads + * and stores are done little endian but from addresses which + * are adjusted by XORing with the appropriate constant. So the + * endianness to use for the raw data access is not affected by + * SCTLR.B. + * In user mode, however, we model BE32 as byte-invariant + * big-endianness (because user-only code cannot tell the + * difference), and so we need to use a data access endianness + * that depends on SCTLR.B. + */ + if (sctlr_b) { + return true; + } +#endif + /* In 32bit endianness is determined by looking at CPSR's E bit */ + return env->uncached_cpsr & CPSR_E; +} + +static inline bool arm_cpu_data_is_big_endian_a64(int el, uint64_t sctlr) +{ + return sctlr & (el ? SCTLR_EE : SCTLR_E0E); +} + +/* Return true if the processor is in big-endian mode. */ +static inline bool arm_cpu_data_is_big_endian(CPUARMState *env) +{ + if (!is_a64(env)) { + return arm_cpu_data_is_big_endian_a32(env, arm_sctlr_b(env)); + } else { + int cur_el = arm_current_el(env); + uint64_t sctlr = arm_sctlr(env, cur_el); + return arm_cpu_data_is_big_endian_a64(cur_el, sctlr); + } +} + +#ifdef CONFIG_USER_ONLY +static inline bool arm_cpu_bswap_data(CPUARMState *env) +{ + return TARGET_BIG_ENDIAN ^ arm_cpu_data_is_big_endian(env); +} +#endif + static inline void aarch64_save_sp(CPUARMState *env, int el) { if (env->pstate & PSTATE_SP) { diff --git a/target/arm/tcg/helper-a64.c b/target/arm/tcg/helper-a64.c index 32f0647ca4..9244848efe 100644 --- a/target/arm/tcg/helper-a64.c +++ b/target/arm/tcg/helper-a64.c @@ -631,6 +631,7 @@ static void cpsr_write_from_spsr_elx(CPUARMState *env, void HELPER(exception_return)(CPUARMState *env, uint64_t new_pc) { + ARMCPU *cpu = env_archcpu(env); int cur_el = arm_current_el(env); unsigned int spsr_idx = aarch64_banked_spsr_index(cur_el); uint32_t spsr = env->banked_spsr[spsr_idx]; @@ -677,12 +678,17 @@ void HELPER(exception_return)(CPUARMState *env, uint64_t new_pc) goto illegal_return; } + if (!return_to_aa64 && !cpu_isar_feature(aa64_aa32, cpu)) { + /* Return to AArch32 when CPU is AArch64-only */ + goto illegal_return; + } + if (new_el == 1 && (arm_hcr_el2_eff(env) & HCR_TGE)) { goto illegal_return; } bql_lock(); - arm_call_pre_el_change_hook(env_archcpu(env)); + arm_call_pre_el_change_hook(cpu); bql_unlock(); if (!return_to_aa64) { @@ -710,7 +716,7 @@ void HELPER(exception_return)(CPUARMState *env, uint64_t new_pc) int tbii; env->aarch64 = true; - spsr &= aarch64_pstate_valid_mask(&env_archcpu(env)->isar); + spsr &= aarch64_pstate_valid_mask(&cpu->isar); pstate_write(env, spsr); if (!arm_singlestep_active(env)) { env->pstate &= ~PSTATE_SS; @@ -749,7 +755,7 @@ void HELPER(exception_return)(CPUARMState *env, uint64_t new_pc) aarch64_sve_change_el(env, cur_el, new_el, return_to_aa64); bql_lock(); - arm_call_el_change_hook(env_archcpu(env)); + arm_call_el_change_hook(cpu); bql_unlock(); return; diff --git a/target/arm/tcg/hflags.c b/target/arm/tcg/hflags.c index 9e6a1869f9..8d79b8b7ae 100644 --- a/target/arm/tcg/hflags.c +++ b/target/arm/tcg/hflags.c @@ -63,6 +63,15 @@ static bool aprofile_require_alignment(CPUARMState *env, int el, uint64_t sctlr) #endif } +bool access_secure_reg(CPUARMState *env) +{ + bool ret = (arm_feature(env, ARM_FEATURE_EL3) && + !arm_el_is_aa64(env, 3) && + !(env->cp15.scr_el3 & SCR_NS)); + + return ret; +} + static CPUARMTBFlags rebuild_hflags_common(CPUARMState *env, int fp_el, ARMMMUIdx mmu_idx, CPUARMTBFlags flags) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 8bef391bb0..39014325df 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -1381,14 +1381,14 @@ static bool fp_access_check_only(DisasContext *s) { if (s->fp_excp_el) { assert(!s->fp_access_checked); - s->fp_access_checked = true; + s->fp_access_checked = -1; gen_exception_insn_el(s, 0, EXCP_UDEF, syn_fp_access_trap(1, 0xe, false, 0), s->fp_excp_el); return false; } - s->fp_access_checked = true; + s->fp_access_checked = 1; return true; } @@ -1456,23 +1456,23 @@ static int fp_access_check_vector_hsd(DisasContext *s, bool is_q, MemOp esz) bool sve_access_check(DisasContext *s) { if (s->pstate_sm || !dc_isar_feature(aa64_sve, s)) { + bool ret; + assert(dc_isar_feature(aa64_sme, s)); - if (!sme_sm_enabled_check(s)) { - goto fail_exit; - } - } else if (s->sve_excp_el) { + ret = sme_sm_enabled_check(s); + s->sve_access_checked = (ret ? 1 : -1); + return ret; + } + if (s->sve_excp_el) { + /* Assert that we only raise one exception per instruction. */ + assert(!s->sve_access_checked); gen_exception_insn_el(s, 0, EXCP_UDEF, syn_sve_access_trap(), s->sve_excp_el); - goto fail_exit; + s->sve_access_checked = -1; + return false; } - s->sve_access_checked = true; + s->sve_access_checked = 1; return fp_access_check(s); - - fail_exit: - /* Assert that we only raise one exception per instruction. */ - assert(!s->sve_access_checked); - s->sve_access_checked = true; - return false; } /* @@ -1500,8 +1500,9 @@ bool sme_enabled_check(DisasContext *s) * sme_excp_el by itself for cpregs access checks. */ if (!s->fp_excp_el || s->sme_excp_el < s->fp_excp_el) { - s->fp_access_checked = true; - return sme_access_check(s); + bool ret = sme_access_check(s); + s->fp_access_checked = (ret ? 1 : -1); + return ret; } return fp_access_check_only(s); } @@ -10257,8 +10258,8 @@ static void aarch64_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) s->insn = insn; s->base.pc_next = pc + 4; - s->fp_access_checked = false; - s->sve_access_checked = false; + s->fp_access_checked = 0; + s->sve_access_checked = 0; if (s->pstate_il) { /* diff --git a/target/arm/tcg/translate-a64.h b/target/arm/tcg/translate-a64.h index 7d3b59ccd9..b2420f59eb 100644 --- a/target/arm/tcg/translate-a64.h +++ b/target/arm/tcg/translate-a64.h @@ -65,7 +65,7 @@ TCGv_i64 gen_mte_checkN(DisasContext *s, TCGv_i64 addr, bool is_write, static inline void assert_fp_access_checked(DisasContext *s) { #ifdef CONFIG_DEBUG_TCG - if (unlikely(!s->fp_access_checked || s->fp_excp_el)) { + if (unlikely(s->fp_access_checked <= 0)) { fprintf(stderr, "target-arm: FP access check missing for " "instruction 0x%08x\n", s->insn); abort(); diff --git a/target/arm/tcg/translate.h b/target/arm/tcg/translate.h index f8dc2f0d4b..53e485d28a 100644 --- a/target/arm/tcg/translate.h +++ b/target/arm/tcg/translate.h @@ -92,15 +92,19 @@ typedef struct DisasContext { bool aarch64; bool thumb; bool lse2; - /* Because unallocated encodings generate different exception syndrome + /* + * Because unallocated encodings generate different exception syndrome * information from traps due to FP being disabled, we can't do a single * "is fp access disabled" check at a high level in the decode tree. * To help in catching bugs where the access check was forgotten in some * code path, we set this flag when the access check is done, and assert * that it is set at the point where we actually touch the FP regs. + * 0: not checked, + * 1: checked, access ok + * -1: checked, access denied */ - bool fp_access_checked; - bool sve_access_checked; + int8_t fp_access_checked; + int8_t sve_access_checked; /* ARMv8 single-step state (this is distinct from the QEMU gdbstub * single-step support). */ diff --git a/util/cacheflush.c b/util/cacheflush.c index a08906155a..1d12899a39 100644 --- a/util/cacheflush.c +++ b/util/cacheflush.c @@ -279,9 +279,11 @@ void flush_idcache_range(uintptr_t rx, uintptr_t rw, size_t len) for (p = rw & -dcache_lsize; p < rw + len; p += dcache_lsize) { asm volatile("dc\tcvau, %0" : : "r" (p) : "memory"); } - asm volatile("dsb\tish" : : : "memory"); } + /* DSB unconditionally to ensure any outstanding writes are committed. */ + asm volatile("dsb\tish" : : : "memory"); + /* * If CTR_EL0.DIC is enabled, Instruction cache cleaning to the Point * of Unification is not required for instruction to data coherence. |